This file is used to prepare the figures for the paper.

library(dplyr)
library(patchwork)
library(ggplot2)
library(ComplexHeatmap)

.libPaths()
## [1] "/usr/local/lib/R/library"

Preparation

Here are the folders where analyses are stored :

data_dir = "./.."
list.files(data_dir)
##  [1] "0_intro"      "1_metadata"   "2_individual" "3_combined"   "4_zoom"      
##  [6] "5_wu"         "6_takahashi"  "7_figures"    "LICENSE"      "README.md"   
## [11] "index.html"   "index_layout" "knit_all.sh"

These are the custom colors for cell populations :

color_markers = readRDS(paste0(data_dir, "/1_metadata/hs_hd_color_markers.rds"))
color_markers = color_markers[names(color_markers) != "melanocytes"] # remove melanocytes

data.frame(cell_type = names(color_markers),
           color = unlist(color_markers)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers), breaks = names(color_markers)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank(),
                 axis.text.x = element_text(angle = 30, hjust = 1))

We define custom colors for sample type :

sample_type_colors = setNames(nm = c("HS", "HD"),
                              c("#C55F40", "#2C78E6"))

data.frame(cell_type = names(sample_type_colors),
           color = unlist(sample_type_colors)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(sample_type_colors), breaks = names(sample_type_colors)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank())

We define custom colors for laboratory :

laboratory_colors = setNames(nm = c("Our", "Wu", "Takahashi"),
                             c("firebrick3", "deepskyblue", "mediumpurple"))

data.frame(cell_type = names(laboratory_colors),
           color = unlist(laboratory_colors)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(laboratory_colors), breaks = names(laboratory_colors)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank())

We define custom colors for location :

location_colors = setNames(nm = c("axillary", "hair scalp", "pubis"),
                           c("forestgreen", "orchid3", "darkorange1"))

data.frame(cell_type = names(location_colors),
           color = unlist(location_colors)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(location_colors), breaks = names(location_colors)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank())

We define custom colors for gender :

gender_colors = setNames(nm = c("F", "M"),
                           c("lightslateblue", "chartreuse3"))

data.frame(cell_type = names(gender_colors),
           color = unlist(gender_colors)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(gender_colors), breaks = names(gender_colors)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank())

We set a background color :

bg_color = "gray94"

This is the correspondence between cell types and cell families, and custom colors to color cells by cell category :

custom_order_cell_type = data.frame(
  cell_type = names(color_markers),
  cell_category = c(rep("immune cells", 5),
                    rep("matrix", 5),
                    rep("non matrix", 5)),
  stringsAsFactors = FALSE)
custom_order_cell_type$cell_type = factor(custom_order_cell_type$cell_type,
                                          levels = custom_order_cell_type$cell_type)
rownames(custom_order_cell_type) = custom_order_cell_type$cell_type
custom_order_cell_type
##                                 cell_type cell_category
## CD4 T cells                   CD4 T cells  immune cells
## CD8 T cells                   CD8 T cells  immune cells
## Langerhans cells         Langerhans cells  immune cells
## macrophages                   macrophages  immune cells
## B cells                           B cells  immune cells
## cuticle                           cuticle        matrix
## cortex                             cortex        matrix
## medulla                           medulla        matrix
## IRS                                   IRS        matrix
## proliferative               proliferative        matrix
## HF-SCs                             HF-SCs    non matrix
## IFE basal                       IFE basal    non matrix
## IFE granular spinous IFE granular spinous    non matrix
## ORS                                   ORS    non matrix
## sebocytes                       sebocytes    non matrix
category_color = c("immune cells" = "slateblue1",
                   "matrix" = "mediumseagreen",
                   "non matrix" = "firebrick3")

We define markers to display on a dotplot to assess cell type annotation :

dotplot_markers = c("PTPRC",                # immune cells
                    "CD3E", "CD4",          # CD4+ T cells
                    "CD3E", "CD8A",         # CD8+ T cells
                    "CD207", "AIF1",        # Langerhans cells
                    "TREM2", "MSR1",        # macrophages
                    "CD79A", "CD79B",       # B cells
                    "MSX2",                 # matrix cells
                    "KRT32", "KRT35",       # cuticle
                    "KRT31", "PRR9",        # cortex
                    "BAMBI", "ALDH1A3",     # medulla
                    "KRT71", "KRT73",       # IRS
                    "TOP2A", "MCM5", "TK1", # cycling cells
                    "KRT14", "CXCL14",      # non-matrix cells
                    "DIO2", "TCEAL2",       # HF-SCs
                    "KRT15", "COL17A1",     # IFE basal
                    "SPINK5", "KRT1",       # ORS
                    "KRT16", "KRT6C",       # IFE granular or spinous
                    "CLMP", "PPARG")        # sebocytes

We copy-paste a subset of the markers associated with cell type annotation :

cell_markers = list(
  #  Immune cells
  "CD4 T cells" = c("CD3D", "CD3G", "CD3E", "CD52", "IL32",
                    "TRBC1", "CD4", "COTL1", "CD40LG", "GPR183", "TNFRSF1B", "FYB1", "LAT"),
  "CD8 T cells" = c("CD3D", "CD3G", "CD3E", "CD52", "IL32",
                    "CTSW", "NCR3", "CD8A", "NKG7", "KLRC1", "TRGC2", "CCL5", "ZNF683"),
  "Langerhans cells" = c("CD207", "LST1", "AIF1", "CPVL", "C15orf48", "CD1A", "CD52", "CD1E", "CD1C"),
  "macrophages" = c("LST1", "AIF1", "C3", "OLFML3", "TREM2", "A2M",
                    "MSR1", "FCGR3A", "VSIG4", "AP003481.1"),
  "B cells" = c("CD79A", "IGHG2", "IGHG3", "IGLC3", "IGHG1", "IGHM", "MS4A1", "IGLC2",
                "IGHGP", "BANK1", "IGHG4", "IGKC", "TNFRSF13C", "CD79B", "KLF2", "GZMB"),
  # Matrix
  "cuticle" = c("KRT32", "CYSTM1", "MT4", "KRT35", "VSNL1", "NOTCH1"),
  "cortex" = c("C10orf99", "CRYAB", "HEPHL1", "ALOX15B", "EFHD1", "DSG4", "DNASE1L2", "TGM3", "KRT31"),
  "medulla" = c("BAMBI", "SLC7A8", "EDNRA", "IGFBP2", "KRT25", "KITLG"),
  "IRS" = c("KRT71", "KRT27", "KRT28", "CTSC", "GATA3", "KRT73", "SCEL"),
  "proliferative" = c("TOP2A", "MKI67", "BIRC5", "TK1", "MCM5"),
  # Non-matrix
  "HF-SCs" = c("KRT15", "DST", "DIO2", "TCEAL2", "SFRP1", "TNC", "WIF1", "FRZB", "LHX2", "COL17A1"),
  "IFE basal" = c("GPX2", "MOXD1", "C19orf48", "ALDH3A1", "CDH13", "LAMB3", "CAVIN1"),
  "IFE granular spinous" = c("DEFB1", "LY6D", "EHF", "PDZK1IP1", "S100A7", "FGFBP1", "SPINK5", "KRT1", "KRTDAP"),
  "ORS" = c("KRT16", "KRT6B", "KRT6C", "TM4SF1", "APOC1", "CBLN2", "HES4", "LYPD3"),
  # other
  "melanocytes" = c("DCT", "KIT", "MLANA", "GPM6B", "EDNRB", "PMEL", "IGFBP7", "MITF", "ZEB2", "APOD"),
  "sebocytes" = c("CLMP", "PPARG", "ACSBG1", "MGST1", "SAA1", "APMAP"))

lengths(cell_markers)
##          CD4 T cells          CD8 T cells     Langerhans cells 
##                   13                   13                    9 
##          macrophages              B cells              cuticle 
##                   10                   16                    6 
##               cortex              medulla                  IRS 
##                    9                    6                    7 
##        proliferative               HF-SCs            IFE basal 
##                    5                   10                    7 
## IFE granular spinous                  ORS          melanocytes 
##                    9                    8                   10 
##            sebocytes 
##                    6

Custom functions to display gene expression on the heatmap :

color_fun = function(one_gene) {
  gene_range = range(ht_annot[, one_gene])
  gene_palette = circlize::colorRamp2(colors = c("#FFFFFF", aquarius::color_gene[-1]),
                                      breaks = seq(from = gene_range[1], to = gene_range[2],
                                                   length.out = length(aquarius::color_gene)))
  return(gene_palette)
}

Load datasets

In a list, we load:

  • Seurat object
  • name of the 2D projection to visualize cells on
  • various third element such as: sample information, trajectory, results from gene analysis…
list_data = list()

## Our dataset
# Atlas of all cells
list_data$our_atlas$sobj = readRDS(paste0(data_dir, "/3_combined/hs_hd_sobj.rds"))
list_data$our_atlas$name2D = "harmony_38_tsne"
list_data$our_atlas$sample_info = readRDS(paste0(data_dir, "/1_metadata/hs_hd_sample_info.rds"))
list_data$our_atlas$sobj
## An object of class Seurat 
## 20003 features across 12111 samples within 1 assay 
## Active assay: RNA (20003 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_38_tsne, RNA_pca_38_umap, harmony, harmony_38_umap, harmony_38_tsne
# ORS + IFEb dataset
list_data$ors_ifeb$sobj = readRDS(paste0(data_dir, "/4_zoom/3_zoom_ors_ifeb/ors_ifeb_sobj.rds"))
list_data$ors_ifeb$name2D = "harmony_20_tsne"
list_data$ors_ifeb$list_results = readRDS(paste0(data_dir, "/4_zoom/3_zoom_ors_ifeb/ors_ifeb_list_results.rds"))
list_data$ors_ifeb$sobj
## An object of class Seurat 
## 16683 features across 3416 samples within 1 assay 
## Active assay: RNA (16683 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne, RNA_pca_20_umap, harmony, harmony_20_umap, harmony_20_tsne
# HF-SCs dataset
list_data$hfsc$sobj = readRDS(paste0(data_dir, "/4_zoom/2_zoom_hfsc/hfsc_sobj.rds"))
list_data$hfsc$name2D = "harmony_24_tsne"
list_data$hfsc$list_results = readRDS(paste0(data_dir, "/4_zoom/2_zoom_hfsc/hfsc_list_results.rds"))
list_data$hfsc$sobj
## An object of class Seurat 
## 15384 features across 1454 samples within 1 assay 
## Active assay: RNA (15384 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_24_tsne, RNA_pca_24_umap, harmony, harmony_24_umap, harmony_24_tsne
# Non matrix cells dataset
list_data$non_matrix$sobj = readRDS(paste0(data_dir, "/4_zoom/5_zoom_non_matrix/non_matrix_sobj_traj_tinga.rds"))
list_data$non_matrix$name2D = "harmony_dm"
list_data$non_matrix$my_traj = readRDS(paste0(data_dir, "/4_zoom/5_zoom_non_matrix/non_matrix_my_traj_tinga.rds"))
list_data$non_matrix$sobj
## An object of class Seurat 
## 17837 features across 5599 samples within 1 assay 
## Active assay: RNA (17837 features, 2000 variable features)
##  8 dimensional reductions calculated: RNA_pca, RNA_pca_18_tsne, RNA_pca_18_umap, harmony, harmony_18_umap, harmony_18_tsne, harmony_dm, harmony_dm_5_umap
# Matrix cells dataset (not considered in the manuscript)
# list_data$matrix$sobj = readRDS(paste0(data_dir, "/4_zoom/6_zoom_matrix/matrix_sobj.rds"))
# list_data$matrix$name2D = "harmony_19_tsne"
# list_data$matrix$list_results = readRDS(paste0(data_dir, "/4_zoom/6_zoom_matrix/matrix_list_results.rds"))
# list_data$matrix$sobj

# Immune cells
list_data$immune$sobj = readRDS(paste0(data_dir, "/4_zoom/1_zoom_immune/immune_cells_sobj.rds"))
list_data$immune$name2D = "harmony_20_tsne"
list_data$immune$list_results = readRDS(paste0(data_dir, "/4_zoom/1_zoom_immune/immune_cells_list_results.rds"))
list_data$immune$sobj
## An object of class Seurat 
## 15121 features across 2329 samples within 1 assay 
## Active assay: RNA (15121 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne, RNA_pca_20_umap, harmony, harmony_20_umap, harmony_20_tsne
## Wu dataset (OEP002321)
# Atlas of all cells
list_data$wu_atlas$sobj = readRDS(paste0(data_dir, "/5_wu/3_combined/wu_sobj.rds"))
list_data$wu_atlas$name2D = "harmony_39_tsne"
list_data$wu_atlas$sample_info = readRDS(paste0(data_dir, "/5_wu/1_metadata/wu_sample_info.rds"))

# ORS + IFEb dataset
list_data$wu_ors_ifeb$sobj = readRDS(paste0(data_dir, "/5_wu/4_ors_ifeb/ors_ifeb_sobj.rds"))
list_data$wu_ors_ifeb$name2D = "harmony_20_tsne"
list_data$wu_ors_ifeb$list_results = readRDS(paste0(data_dir, "/5_wu/4_ors_ifeb/ors_ifeb_list_results.rds"))
list_data$wu_ors_ifeb$sobj
## An object of class Seurat 
## 15543 features across 7987 samples within 1 assay 
## Active assay: RNA (15543 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_19_tsne, RNA_pca_19_umap, harmony, harmony_19_umap, harmony_19_tsne
## Takahashi dataset (GSE129611)
# Atlas of all cells
list_data$takahashi_atlas$sobj = readRDS(paste0(data_dir, "/6_takahashi/3_combined/takahashi_sobj.rds"))
list_data$takahashi_atlas$name2D = "harmony_41_tsne"
list_data$takahashi_atlas$sample_info = readRDS(paste0(data_dir, "/6_takahashi/1_metadata/takahashi_sample_info.rds"))
list_data$takahashi_atlas$sobj
## An object of class Seurat 
## 18588 features across 5636 samples within 1 assay 
## Active assay: RNA (18588 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_41_tsne, RNA_pca_41_umap, harmony, harmony_41_umap, harmony_41_tsne
# ORS + IFEb dataset
list_data$takahashi_ors_ifeb$sobj = readRDS(paste0(data_dir, "/6_takahashi/4_ors_ifeb/ors_ifeb_sobj.rds"))
list_data$takahashi_ors_ifeb$name2D = "harmony_23_tsne"
list_data$takahashi_ors_ifeb$list_results = readRDS(paste0(data_dir, "/6_takahashi/4_ors_ifeb/ors_ifeb_list_results.rds"))
list_data$takahashi_ors_ifeb$sobj
## An object of class Seurat 
## 14911 features across 1562 samples within 1 assay 
## Active assay: RNA (14911 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_23_tsne, RNA_pca_23_umap, harmony, harmony_23_umap, harmony_23_tsne
## Three datasets combined (Our + OEP002321 + GSE129611)
list_data$combined$sobj = readRDS(paste0(data_dir, "/3_combined/data3_sobj.rds"))
list_data$combined$name2D = "harmony_37_tsne"
list_data$combined$sobj
## An object of class Seurat 
## 19849 features across 35289 samples within 1 assay 
## Active assay: RNA (19849 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_37_tsne, RNA_pca_37_umap, harmony, harmony_37_umap, harmony_37_tsne
## Print
str(list_data, max.level = 2)
## List of 10
##  $ our_atlas         :List of 3
##   ..$ sobj       :Formal class 'Seurat' [package "Seurat"] with 12 slots
##   ..$ name2D     : chr "harmony_38_tsne"
##   ..$ sample_info:'data.frame':  7 obs. of  8 variables:
##  $ ors_ifeb          :List of 3
##   ..$ sobj        :Formal class 'Seurat' [package "Seurat"] with 12 slots
##   ..$ name2D      : chr "harmony_20_tsne"
##   ..$ list_results:List of 4
##  $ hfsc              :List of 3
##   ..$ sobj        :Formal class 'Seurat' [package "Seurat"] with 12 slots
##   ..$ name2D      : chr "harmony_24_tsne"
##   ..$ list_results:List of 11
##  $ non_matrix        :List of 3
##   ..$ sobj   :Formal class 'Seurat' [package "Seurat"] with 12 slots
##   ..$ name2D : chr "harmony_dm"
##   ..$ my_traj:List of 18
##   .. ..- attr(*, "class")= chr [1:4] "dynwrap::with_dimred" "dynwrap::with_trajectory" "dynwrap::data_wrapper" "list"
##  $ immune            :List of 3
##   ..$ sobj        :Formal class 'Seurat' [package "Seurat"] with 12 slots
##   ..$ name2D      : chr "harmony_20_tsne"
##   ..$ list_results:List of 4
##  $ wu_atlas          :List of 3
##   ..$ sobj       :Formal class 'Seurat' [package "Seurat"] with 12 slots
##   ..$ name2D     : chr "harmony_39_tsne"
##   ..$ sample_info:'data.frame':  6 obs. of  8 variables:
##  $ wu_ors_ifeb       :List of 3
##   ..$ sobj        :Formal class 'Seurat' [package "Seurat"] with 12 slots
##   ..$ name2D      : chr "harmony_20_tsne"
##   ..$ list_results:List of 1
##  $ takahashi_atlas   :List of 3
##   ..$ sobj       :Formal class 'Seurat' [package "Seurat"] with 12 slots
##   ..$ name2D     : chr "harmony_41_tsne"
##   ..$ sample_info:'data.frame':  5 obs. of  8 variables:
##  $ takahashi_ors_ifeb:List of 3
##   ..$ sobj        :Formal class 'Seurat' [package "Seurat"] with 12 slots
##   ..$ name2D      : chr "harmony_23_tsne"
##   ..$ list_results:List of 1
##  $ combined          :List of 2
##   ..$ sobj  :Formal class 'Seurat' [package "Seurat"] with 12 slots
##   ..$ name2D: chr "harmony_37_tsne"

Figures

Sample information

Our dataset:

sobj = list_data$our_atlas$sobj
sample_info = list_data$our_atlas$sample_info

# Nb cells by dataset
to_plot = table(sobj$sample_identifier) %>%
  as.data.frame.table(., stringsAsFactors = FALSE) %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  dplyr::left_join(x = ., y = sample_info, by = "sample_identifier") 

# patchwork
plot_list = aquarius::fig_plot_gb(to_plot, title = "Available datasets")
patchwork::wrap_plots(plot_list) +
  patchwork::plot_layout(design = "A\nB", heights = c(0.1,5)) &
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 15))

Wu dataset:

sobj = list_data$wu_atlas$sobj
sample_info = list_data$wu_atlas$sample_info

# Nb cells by dataset
to_plot = table(sobj$sample_identifier) %>%
  as.data.frame.table(., stringsAsFactors = FALSE) %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  dplyr::left_join(x = ., y = sample_info, by = "sample_identifier") 

# patchwork
plot_list = aquarius::fig_plot_gb(to_plot, title = "Available datasets")
patchwork::wrap_plots(plot_list) +
  patchwork::plot_layout(design = "A\nB", heights = c(0.1,5)) &
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 15))

Takahashi dataset:

sobj = list_data$takahashi_atlas$sobj
sample_info = list_data$takahashi_atlas$sample_info

# Nb cells by dataset
to_plot = table(sobj$sample_identifier) %>%
  as.data.frame.table(., stringsAsFactors = FALSE) %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  dplyr::left_join(x = ., y = sample_info, by = "sample_identifier") 

# patchwork
plot_list = aquarius::fig_plot_gb(to_plot, title = "Available datasets")
patchwork::wrap_plots(plot_list) +
  patchwork::plot_layout(design = "A\nB", heights = c(0.1,5)) &
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 15))

Annotation smoothing

For each dataset, we smooth the single-cell level cell type annotation at a cluster level.

  • Our dataset:
# Select dataset
sobj = list_data$our_atlas$sobj

# Annotation smoothing
sobj$cell_type = sobj$cell_type %>%
  as.character() %>%
  factor(., levels = names(color_markers))

cluster_type = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj$cell_type)[cluster_type])

sobj$cluster_type = cluster_type[sobj$seurat_clusters]
sobj$cluster_type = factor(sobj$cluster_type,
                           levels = levels(sobj$cell_type))

# Consider change
list_data$our_atlas$sobj = sobj
# Select dataset
sobj = list_data$non_matrix$sobj

# Annotation smoothing
sobj$cell_type = sobj$cell_type %>%
  as.character() %>%
  factor(., levels = names(color_markers))

cluster_type = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj$cell_type)[cluster_type])

sobj$cluster_type = cluster_type[sobj$seurat_clusters]
sobj$cluster_type = factor(sobj$cluster_type,
                           levels = levels(sobj$cell_type))

# Consider change
list_data$non_matrix$sobj = sobj
# Select dataset
sobj = list_data$immune$sobj

# Annotation smoothing
sobj$cell_type = sobj$cell_type %>%
  as.character() %>%
  factor(., levels = names(color_markers))

cluster_type = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj$cell_type)[cluster_type])

sobj$cluster_type = cluster_type[sobj$seurat_clusters]
sobj$cluster_type = factor(sobj$cluster_type,
                           levels = levels(sobj$cell_type))

# Consider change
list_data$immune$sobj = sobj
  • Wu dataset:
# Select dataset
sobj = list_data$wu_atlas$sobj

# Annotation smoothing
sobj$cell_type = sobj$cell_type %>%
  as.character() %>%
  factor(., levels = names(color_markers))

cluster_type = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj$cell_type)[cluster_type])

sobj$cluster_type = cluster_type[sobj$seurat_clusters]
sobj$cluster_type = factor(sobj$cluster_type,
                           levels = levels(sobj$cell_type))

# Consider change
list_data$wu_atlas$sobj = sobj
  • Takahashi dataset:
# Select dataset
sobj = list_data$takahashi_atlas$sobj

# Annotation smoothing
sobj$cell_type = sobj$cell_type %>%
  as.character() %>%
  factor(., levels = names(color_markers))

cluster_type = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj$cell_type)[cluster_type])

sobj$cluster_type = cluster_type[sobj$seurat_clusters]
sobj$cluster_type = factor(sobj$cluster_type,
                           levels = levels(sobj$cell_type))

# Consider change
list_data$takahashi_atlas$sobj = sobj
  • Combined dataset:
# Select dataset
sobj = list_data$combined$sobj

# Annotation smoothing
sobj$cell_type = sobj$cell_type %>%
  as.character() %>%
  factor(., levels = names(color_markers))

cluster_type = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj$cell_type)[cluster_type])

sobj$cluster_type = cluster_type[sobj$seurat_clusters]
sobj$cluster_type = factor(sobj$cluster_type,
                           levels = levels(sobj$cell_type))

# Consider change
list_data$combined$sobj = sobj

For the combined dataset, we also define the cell category:

# Select dataset
sobj = list_data$combined$sobj

# Define cell category
sobj$cell_category = custom_order_cell_type[sobj$cell_type, "cell_category"]
table(sobj$cell_type, sobj$cell_category)
##                       
##                        immune cells matrix non matrix
##   CD4 T cells                  1059      0          0
##   CD8 T cells                   652      0          0
##   Langerhans cells              461      0          0
##   macrophages                   571      0          0
##   B cells                       207      0          0
##   cuticle                         0   3021          0
##   cortex                          0   2163          0
##   medulla                         0    903          0
##   IRS                             0   1152          0
##   proliferative                   0   2590          0
##   HF-SCs                          0      0       3292
##   IFE basal                       0      0       4117
##   IFE granular spinous            0      0       5349
##   ORS                             0      0       9121
##   sebocytes                       0      0        631
# Consider change
list_data$combined$sobj = sobj

Atlas

Our dataset

We select the dataset:

sobj = list_data$our_atlas$sobj
name2D = list_data$our_atlas$name2D
sample_info = list_data$our_atlas$sample_info
  • sample identifier
# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj), replace = FALSE, size = ncol(sobj))

# Extract coordinates
cells_coord = sobj@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$sample_identifier = sobj$sample_identifier
cells_coord$location = sobj$location
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = sample_identifier)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$sample_identifier) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

  • location
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = location)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = location_colors,
                              breaks = names(location_colors)) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

  • cell type annotation
Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

  • cluster type annotation
Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

  • dotplot for cell type annotation
plot_list = aquarius::plot_dotplot(sobj,
                                   markers = dotplot_markers,
                                   assay = "RNA", column_name = "cell_type", nb_hline = 0) +
  ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
  ggplot2::theme(legend.position = "left",
                 legend.justification = "bottom",
                 legend.box = "vertical",
                 legend.box.margin = margin(0,70,0,0),
                 axis.title = element_blank(),
                 axis.ticks.x = element_blank(),
                 axis.text.x = element_blank(),
                 axis.line.x = element_blank(),
                 plot.margin = unit(rep(0, 4), "cm"))

p = data.frame(cell_type = factor(levels(sobj$cell_type),
                                  levels = levels(sobj$cell_type))) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0)) +
  ggplot2::geom_point(size = 0) +
  ggplot2::geom_segment(aes(x = 0.5, xend = 5.5, y = 0, yend = 0), size = 6, col = category_color["immune cells"]) +
  ggplot2::geom_segment(aes(x = 5.5, xend = 10.5, y = 0, yend = 0), size = 6, col = category_color["matrix"]) +
  ggplot2::geom_segment(aes(x = 10.5, xend = 15.5, y = 0, yend = 0), size = 6, col = category_color["non matrix"]) +
  ggplot2::scale_y_continuous(expand = c(0,0), limits = c(0,0)) +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.text.y = element_blank(),
                 axis.ticks.y = element_blank(),
                 axis.title = element_blank(),
                 axis.line.y = element_blank(),
                 axis.text.x = element_text(angle = 45, hjust = 1, size = 10, color = "black"),
                 plot.margin = unit(c(0,0.5,0.5,0), "cm"))

plot_list = patchwork::wrap_plots(plot_list, p,
                                  ncol = 1, heights = c(25, 1))
plot_list

  • barplot with proportion of cells for cell types of interest, by sample ID
# Cells of interest
cells_oi = c("CD4 T cells", "macrophages")

# Proportion of cells by sample ID
df_proportion = table(sobj$cluster_type,
                      sobj$sample_identifier) %>%
  prop.table(., margin = 2) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("cluster_type", "sample_identifier", "freq")) %>%
  dplyr::filter(cluster_type %in% cells_oi) %>%
  dplyr::mutate(cluster_type = factor(cluster_type, levels = cells_oi)) %>%
  dplyr::mutate(freq = 100*freq) # percentage

# Plot
ggplot2::ggplot(df_proportion, aes(x = sample_identifier,
                                   y = freq,
                                   fill = cluster_type)) +
  ggplot2::geom_bar(stat = "identity", position = ggplot2::position_dodge(),
                    color = "black", width = 0.7) +
  ggplot2::labs(y = "% of cells by sample") +
  ggplot2::scale_y_continuous(expand = c(0, 0), limits = c(0,14)) +
  ggplot2::scale_fill_manual(values = color_markers[cells_oi],
                             breaks = cells_oi,
                             name = "Cell Type") +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.title.x = element_blank(),
                 axis.line.x = element_line(color = "gray50"),
                 panel.grid.major.y = element_line(),
                 panel.grid.minor.y = element_line(),
                 text = element_text(size = 15))

Wu dataset

We select the dataset:

sobj = list_data$wu_atlas$sobj
name2D = list_data$wu_atlas$name2D
sample_info = list_data$wu_atlas$sample_info
  • sample identifier
# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj), replace = FALSE, size = ncol(sobj))

# Extract coordinates
cells_coord = sobj@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$sample_identifier = sobj$sample_identifier
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = sample_identifier)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$sample_identifier) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

  • cell type annotation
Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

  • cluster type annotation
Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

  • dotplot for cell type annotation
plot_list = aquarius::plot_dotplot(sobj,
                                   markers = dotplot_markers,
                                   assay = "RNA", column_name = "cell_type", nb_hline = 0) +
  ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
  ggplot2::theme(legend.position = "left",
                 legend.justification = "bottom",
                 legend.box = "vertical",
                 legend.box.margin = margin(0,70,0,0),
                 axis.title = element_blank(),
                 axis.ticks.x = element_blank(),
                 axis.text.x = element_blank(),
                 axis.line.x = element_blank(),
                 plot.margin = unit(rep(0, 4), "cm"))

p = data.frame(cell_type = factor(levels(sobj$cell_type),
                                  levels = levels(sobj$cell_type))) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0)) +
  ggplot2::geom_point(size = 0) +
  ggplot2::geom_segment(aes(x = 0.5, xend = 5.5, y = 0, yend = 0), size = 6, col = category_color["immune cells"]) +
  ggplot2::geom_segment(aes(x = 5.5, xend = 10.5, y = 0, yend = 0), size = 6, col = category_color["matrix"]) +
  ggplot2::geom_segment(aes(x = 10.5, xend = 15.5, y = 0, yend = 0), size = 6, col = category_color["non matrix"]) +
  ggplot2::scale_y_continuous(expand = c(0,0), limits = c(0,0)) +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.text.y = element_blank(),
                 axis.ticks.y = element_blank(),
                 axis.title = element_blank(),
                 axis.line.y = element_blank(),
                 axis.text.x = element_text(angle = 45, hjust = 1, size = 10, color = "black"),
                 plot.margin = unit(c(0,0.5,0.5,0), "cm"))

plot_list = patchwork::wrap_plots(plot_list, p,
                                  ncol = 1, heights = c(25, 1))
plot_list

Takahashi dataset

We select the dataset:

sobj = list_data$takahashi_atlas$sobj
name2D = list_data$takahashi_atlas$name2D
sample_info = list_data$takahashi_atlas$sample_info
  • sample identifier
# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj), replace = FALSE, size = ncol(sobj))

# Extract coordinates
cells_coord = sobj@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$sample_identifier = sobj$sample_identifier
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = sample_identifier)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$sample_identifier) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

  • cell type annotation
Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

  • cluster type annotation
Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

  • dotplot for cell type annotation
plot_list = aquarius::plot_dotplot(sobj,
                                   markers = dotplot_markers,
                                   assay = "RNA", column_name = "cell_type", nb_hline = 0) +
  ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
  ggplot2::theme(legend.position = "left",
                 legend.justification = "bottom",
                 legend.box = "vertical",
                 legend.box.margin = margin(0,70,0,0),
                 axis.title = element_blank(),
                 axis.ticks.x = element_blank(),
                 axis.text.x = element_blank(),
                 axis.line.x = element_blank(),
                 plot.margin = unit(rep(0, 4), "cm"))

p = data.frame(cell_type = factor(levels(sobj$cell_type),
                                  levels = levels(sobj$cell_type))) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0)) +
  ggplot2::geom_point(size = 0) +
  ggplot2::geom_segment(aes(x = 0.5, xend = 5.5, y = 0, yend = 0), size = 6, col = category_color["immune cells"]) +
  ggplot2::geom_segment(aes(x = 5.5, xend = 10.5, y = 0, yend = 0), size = 6, col = category_color["matrix"]) +
  ggplot2::geom_segment(aes(x = 10.5, xend = 15.5, y = 0, yend = 0), size = 6, col = category_color["non matrix"]) +
  ggplot2::scale_y_continuous(expand = c(0,0), limits = c(0,0)) +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.text.y = element_blank(),
                 axis.ticks.y = element_blank(),
                 axis.title = element_blank(),
                 axis.line.y = element_blank(),
                 axis.text.x = element_text(angle = 45, hjust = 1, size = 10, color = "black"),
                 plot.margin = unit(c(0,0.5,0.5,0), "cm"))

plot_list = patchwork::wrap_plots(plot_list, p,
                                  ncol = 1, heights = c(25, 1))
plot_list

Combined datasets

We select the dataset:

sobj = list_data$combined$sobj
name2D = list_data$combined$name2D
sample_info = rbind(list_data$our_atlas$sample_info,
                    list_data$wu_atlas$sample_info,
                    list_data$takahashi_atlas$sample_info)
  • sample identifier
# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj), replace = FALSE, size = ncol(sobj))

# Extract coordinates
cells_coord = sobj@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$sample_identifier = sobj$sample_identifier
cells_coord$location = sobj$location
cells_coord$laboratory = sobj$laboratory
cells_coord$gender = dplyr::left_join(sobj@meta.data,
                                      sample_info,
                                      by = "project_name")[, "gender"]
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = sample_identifier)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$sample_identifier) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

  • location
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = location)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = location_colors,
                              breaks = names(location_colors)) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

  • laboratory
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = laboratory)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = laboratory_colors,
                              breaks = names(laboratory_colors)) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

  • gender
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = gender)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = gender_colors,
                              breaks = names(gender_colors)) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

  • cell type annotation
Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

  • cluster type annotation
Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

  • split by laboratory:
plot_list = aquarius::plot_split_dimred(sobj,
                                        reduction = name2D,
                                        split_by = "laboratory",
                                        group_by = "cluster_type",
                                        group_color = color_markers,
                                        order = TRUE,
                                        bg_color = bg_color)

patchwork::wrap_plots(plot_list, nrow = 1) +
  patchwork::plot_layout(guides = "collect") &
  Seurat::NoLegend()

  • gene to assess global annotation
plot_list = lapply(c("PTPRC", "MSX2", "KRT14"), FUN = function(one_gene) {
  p = Seurat::FeaturePlot(sobj, features = one_gene, reduction = name2D) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    ggplot2::theme(aspect.ratio = 1) +
    Seurat::NoAxes() +
    Seurat::NoLegend()
  
  return(p)
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

  • violin plot, split by cell type, split by laboratory

for non matrix cells

category_of_interest = "non matrix"

# Select cells of interest
cell_type_of_interest = as.character(custom_order_cell_type[
  which(custom_order_cell_type$cell_category == category_of_interest), "cell_type"])

subsobj_of_interest = subset(sobj, cell_type %in% cell_type_of_interest)
subsobj_of_interest$cell_type_labo = paste0(subsobj_of_interest$cell_type,
                                            subsobj_of_interest$laboratory)

cell_markers_of_interest = cell_markers[cell_type_of_interest]

total_height = max(lengths(cell_markers_of_interest)) + 1

# Violin plot
patchwork_list = lapply(names(cell_markers_of_interest), FUN = function(cell_type) {
  markers_set = cell_markers_of_interest[[cell_type]]
  
  # Individual violin plot
  sub_plot_list = lapply(markers_set, FUN = function(one_gene) {
    p = Seurat::VlnPlot(subsobj_of_interest,
                        features = one_gene, pt.size = 0,
                        group.by = "cell_type",
                        split.by = "laboratory",
                        cols = laboratory_colors,
                        combine = FALSE)[[1]] +
      ggplot2::labs(y = one_gene) +
      ggplot2::scale_y_continuous(expand = c(0.01, 0)) +
      ggplot2::geom_vline(mapping = NULL, data = NULL,
                          xintercept = c(1:4) + 0.5,
                          col = "gray50", lty = 2) +
      Seurat::NoLegend() +
      ggplot2::theme(plot.title = element_blank(),
                     axis.title.x = element_blank(), # "Identity"
                     axis.line.x = element_blank(),  # replaced by panel.border
                     axis.title.y = element_text(size = 8, angle = 0, vjust = 0.5), # gene
                     axis.text.y = element_blank(), # "Expression level"
                     axis.text.x = element_blank(), # cell type
                     axis.ticks = element_blank(),
                     plot.margin = unit(c(0,0,0,0), "cm"),
                     panel.border = element_rect(size = 1, color = "gray50"))
    return(p)
  })
  
  # Reset axis.text for last plot
  # sub_plot_list[[length(sub_plot_list)]] = sub_plot_list[[length(sub_plot_list)]] +
  #   ggplot2::theme(axis.text.x = element_text(angle = 90, vjust = 0.5)) # cell type
  
  # Add empty plot to arrange heights
  sub_plot_list[[length(sub_plot_list) + 1]] = patchwork::plot_spacer()
  empty_plot_height = total_height - length(markers_set)
  
  # Arrange as patchwork
  pp = patchwork::wrap_plots(sub_plot_list,
                             ncol = 1,
                             heights = c(rep(1, length(markers_set)),
                                         empty_plot_height)) +
    patchwork::plot_annotation(title = cell_type)
  
  return(pp)
})
names(patchwork_list) = names(cell_markers_of_interest)

patchwork::wrap_plots(patchwork_list, ncol = 1)

for matrix cells

category_of_interest = "matrix"

# Select cells of interest
cell_type_of_interest = as.character(custom_order_cell_type[
  which(custom_order_cell_type$cell_category == category_of_interest), "cell_type"])

subsobj_of_interest = subset(sobj, cell_type %in% cell_type_of_interest)
subsobj_of_interest$cell_type_labo = paste0(subsobj_of_interest$cell_type,
                                            subsobj_of_interest$laboratory)

cell_markers_of_interest = cell_markers[cell_type_of_interest]

total_height = max(lengths(cell_markers_of_interest)) + 1

# Violin plot
patchwork_list = lapply(names(cell_markers_of_interest), FUN = function(cell_type) {
  markers_set = cell_markers_of_interest[[cell_type]]
  
  # Individual violin plot
  sub_plot_list = lapply(markers_set, FUN = function(one_gene) {
    p = Seurat::VlnPlot(subsobj_of_interest,
                        features = one_gene, pt.size = 0,
                        group.by = "cell_type",
                        split.by = "laboratory",
                        cols = laboratory_colors,
                        combine = FALSE)[[1]] +
      ggplot2::labs(y = one_gene) +
      ggplot2::scale_y_continuous(expand = c(0.01, 0)) +
      ggplot2::geom_vline(mapping = NULL, data = NULL,
                          xintercept = c(1:4) + 0.5,
                          col = "gray50", lty = 2) +
      Seurat::NoLegend() +
      ggplot2::theme(plot.title = element_blank(),
                     axis.title.x = element_blank(), # "Identity"
                     axis.line.x = element_blank(),  # replaced by panel.border
                     axis.title.y = element_text(size = 8, angle = 0, vjust = 0.5), # gene
                     axis.text.y = element_blank(), # "Expression level"
                     axis.text.x = element_blank(), # cell type
                     axis.ticks = element_blank(),
                     plot.margin = unit(c(0,0,0,0), "cm"),
                     panel.border = element_rect(size = 1, color = "gray50"))
    return(p)
  })
  
  # Reset axis.text for last plot
  # sub_plot_list[[length(sub_plot_list)]] = sub_plot_list[[length(sub_plot_list)]] +
  #   ggplot2::theme(axis.text.x = element_text(angle = 90, vjust = 0.5)) # cell type
  
  # Add empty plot to arrange heights
  sub_plot_list[[length(sub_plot_list) + 1]] = patchwork::plot_spacer()
  empty_plot_height = total_height - length(markers_set)
  
  # Arrange as patchwork
  pp = patchwork::wrap_plots(sub_plot_list,
                             ncol = 1,
                             heights = c(rep(1, length(markers_set)),
                                         empty_plot_height)) +
    patchwork::plot_annotation(title = cell_type)
  
  return(pp)
})
names(patchwork_list) = names(cell_markers_of_interest)

patchwork::wrap_plots(patchwork_list, ncol = 1)

Non-matrix cells

We select the datasets:

sobj_atlas = list_data$our_atlas$sobj
name2D_atlas = list_data$our_atlas$name2D
sobj = list_data$non_matrix$sobj
name2D = list_data$non_matrix$name2D
my_traj = list_data$non_matrix$my_traj

cell_type_of_interest = c("HF-SCs", "ORS", "IFE basal", "IFE granular spinous")

Location of cells on the whole atlas:

sobj_atlas$cell_bc = colnames(sobj_atlas)
sobj$cell_bc = colnames(sobj)
sobj_atlas$is_of_interest = dplyr::left_join(sobj_atlas@meta.data[, c("cell_bc", "percent.mt")],
                                             sobj@meta.data[, c("cell_bc", "cluster_type")],
                                             by = "cell_bc")[, "cluster_type"]

Seurat::DimPlot(sobj_atlas, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_of_interest", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(color_markers[cell_type_of_interest], bg_color),
                              breaks = c(cell_type_of_interest, NA), na.value = bg_color) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_blank()) +
  Seurat::NoAxes() + Seurat::NoLegend()

  • sample type
Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "sample_type", cols = sample_type_colors) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

  • cell type annotation
Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

  • cluster type annotation
Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

  • pseudotime
Seurat::FeaturePlot(sobj, reduction = name2D, pt.size = 0.5,
                    features = "pseudotime") +
  ggplot2::scale_color_gradientn(colors = viridis::viridis(n = 100)) +
  ggplot2::lims(x = range(sobj@reductions[[name2D]]@cell.embeddings[, 1]),
                y = range(sobj@reductions[[name2D]]@cell.embeddings[, 2])) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_blank(),
                 legend.position = "bottom",
                 legend.direction = "horizontal") +
  Seurat::NoAxes()

  • pseudotime with dynplot’s function :
dynplot::plot_dimred(trajectory = my_traj,
                     dimred = sobj[[name2D]]@cell.embeddings,
                     # Cells
                     color_cells = 'pseudotime',
                     size_cells = 0.5,
                     border_radius_percentage = 0,
                     # Trajectory
                     plot_trajectory = TRUE,
                     color_trajectory = "none",
                     label_milestones = FALSE,
                     size_milestones = 0,
                     size_transitions = 1)

  • violin plots
features_oi = c("SOX9", "TCF4", "TBX1", "NFATC1", 
                "LHX2", "RUNX1", "VIM", "TCF3", "FGF18", "CD34",
                # HF-SCs + IFE basal
                "COL17A1", "MT2A", "TXNIP", "CAVIN1",
                # HF-SCs + ORS
                "TUBB2A", "KRT6B", "MARCKSL1", "ID4", "CYP1B1", "BARX2",
                # HF-SCs
                "LMCD1", "TCEAL2", "FRZB", "THBS1", "DIO2")

plot_list = lapply(features_oi, FUN = function(one_gene) {
  p = Seurat::VlnPlot(sobj, group.by = "cluster_type",
                      features = one_gene, pt.size = 0.001) +
    ggplot2::scale_fill_manual(values = color_markers[levels(sobj$cluster_type)],
                               breaks = levels(sobj$cluster_type)) +
    ggplot2::theme(plot.subtitle = element_text(hjust = 0.5),
                   axis.title.x = element_blank(),
                   legend.position = "none")
  return(p)
})

names(plot_list) = features_oi
plot_list
## $SOX9

## 
## $TCF4

## 
## $TBX1

## 
## $NFATC1

## 
## $LHX2

## 
## $RUNX1

## 
## $VIM

## 
## $TCF3

## 
## $FGF18

## 
## $CD34

## 
## $COL17A1

## 
## $MT2A

## 
## $TXNIP

## 
## $CAVIN1

## 
## $TUBB2A

## 
## $KRT6B

## 
## $MARCKSL1

## 
## $ID4

## 
## $CYP1B1

## 
## $BARX2

## 
## $LMCD1

## 
## $TCEAL2

## 
## $FRZB

## 
## $THBS1

## 
## $DIO2

  • feature plots
plot_list = lapply(features_oi, FUN = function(one_gene) {
  p = Seurat::FeaturePlot(sobj, features = one_gene, reduction = name2D) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    ggplot2::lims(x = range(sobj@reductions[[name2D]]@cell.embeddings[, 1]),
                  y = range(sobj@reductions[[name2D]]@cell.embeddings[, 2])) +
    ggplot2::theme(aspect.ratio = 1) +
    Seurat::NoAxes() +
    Seurat::NoLegend()
  
  return(p)
})

names(plot_list) = features_oi
plot_list
## $SOX9

## 
## $TCF4

## 
## $TBX1

## 
## $NFATC1

## 
## $LHX2

## 
## $RUNX1

## 
## $VIM

## 
## $TCF3

## 
## $FGF18

## 
## $CD34

## 
## $COL17A1

## 
## $MT2A

## 
## $TXNIP

## 
## $CAVIN1

## 
## $TUBB2A

## 
## $KRT6B

## 
## $MARCKSL1

## 
## $ID4

## 
## $CYP1B1

## 
## $BARX2

## 
## $LMCD1

## 
## $TCEAL2

## 
## $FRZB

## 
## $THBS1

## 
## $DIO2

ORS vs IFE basal

Our dataset

We select the dataset:

sobj_atlas = list_data$our_atlas$sobj
name2D_atlas = list_data$our_atlas$name2D
sobj = list_data$ors_ifeb$sobj
name2D = list_data$ors_ifeb$name2D
list_results = list_data$ors_ifeb$list_results

Location of cells on the whole atlas:

sobj_atlas$cell_bc = colnames(sobj_atlas)
sobj$cell_bc = colnames(sobj)
sobj_atlas$is_of_interest = dplyr::left_join(sobj_atlas@meta.data[, c("cell_bc", "percent.mt")],
                                             sobj@meta.data[, c("cell_bc", "cell_type")],
                                             by = "cell_bc")[, "cell_type"]

Seurat::DimPlot(sobj_atlas, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_of_interest", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(color_markers[cell_type_of_interest], bg_color),
                              breaks = c(cell_type_of_interest, NA), na.value = bg_color) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_blank()) +
  Seurat::NoAxes() + Seurat::NoLegend()

DE genes between ORS and IFE basal :

mark = list_results$ORS_vs_IFE_basal$mark
mark$gene_name = rownames(mark)
mark_label = rbind(
  # up-regulated in ORS
  mark %>% dplyr::top_n(., n = 20, wt = avg_logFC),
  # up-regulated in IFE basal
  mark %>% dplyr::top_n(., n = 20, wt = -avg_logFC),
  # representative and selective for ORS
  mark %>% dplyr::top_n(., n = 20, wt = (pct.1 - pct.2)),
  # representative and selective for IFE basal
  mark %>% dplyr::top_n(., n = 20, wt = -(pct.1 - pct.2))) %>%
  dplyr::distinct()
mark_label = mark_label[!grepl(rownames(mark_label), pattern = "^MT"), ]

avg_logFC_range = setNames(c(min(mark_label$avg_logFC), -1, 0, 1, max(mark_label$avg_logFC)),
                           nm = c("dodgerblue4", "dodgerblue3", "#B7B7B7", "firebrick3", "firebrick4"))


ggplot2::ggplot(mark, aes(x = pct.1, y = pct.2, col = avg_logFC)) +
  ggplot2::geom_abline(slope = 1, intercept = 0, lty = 2) +
  ggplot2::geom_point() +
  ggrepel::geom_label_repel(data = mark_label, max.overlaps = Inf,
                            aes(x = pct.1, y = pct.2, label = gene_name),
                            col = "black", fill = NA, size = 3.5, label.size = NA) +
  ggplot2::labs(x = "Enriched in ORS",
                y = "Enriched in IFE basal") +
  ggplot2::scale_color_gradientn(colors = names(avg_logFC_range),
                                 values = scales::rescale(unname(avg_logFC_range))) +
  ggplot2::theme_classic() +
  ggplot2::theme(aspect.ratio = 1)

  • GSEA, enriched in ORS compared to IFE basal
the_gs_name = "REACTOME_KERATINIZATION" 
the_content = list_results$ORS_vs_IFE_basal$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      "\t|\tpvalue : ", round(the_content$pvalue, 4),
                      "\t|\tset size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$ORS_vs_IFE_basal$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

  • GSEA, enriched in IFE basal compared to ORS
the_gs_name = "HALLMARK_INTERFERON_GAMMA_RESPONSE" 
the_content = list_results$ORS_vs_IFE_basal$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      "\t|\tpvalue : ", round(the_content$pvalue, 4),
                      "\t|\tset size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$ORS_vs_IFE_basal$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

  • violin plots of DE genes in ORS, between HS and HD
subsobj = subset(sobj, cell_type == "ORS")
features_oi = c("DUSP1", "DDIT4", "GABARAP", "ZNF302",
                "MIF", "LGALS7", "ARF5", "S100A9")

# Plot !
plot_list = lapply(features_oi, FUN = function(feature) {
  # t-test between sample type
  feature_expr = subsobj@assays$RNA@data[feature, ]
  feature_hs = feature_expr[subsobj$sample_type == "HS"]
  feature_hd = feature_expr[subsobj$sample_type == "HD"]
  feature_hs_VS_feature_hd = stats::t.test(feature_hs, feature_hd)
  feature_hs_VS_feature_hd
  
  # Significance associated with p-value
  pval = feature_hs_VS_feature_hd$p.value
  
  significance = case_when(pval > 0.05 ~ "ns",
                           pval > 0.01 & pval <= 0.05 ~ "*",
                           pval > 0.001 & pval <= 0.01 ~ "**",
                           pval <= 0.001 ~ "***")
  
  # Plot
  p = Seurat::VlnPlot(subsobj, group.by = "sample_type",
                      pt.size = 0.3, features = feature) +
    ggplot2::scale_fill_manual(values = sample_type_colors,
                               breaks = names(sample_type_colors)) +
    # Significance bar
    ggplot2::geom_segment(data = data.frame(1), inherit.aes = FALSE,
                          size = 0.5,
                          x = 1, xend = 2,
                          y = max(feature_expr)+0.3, yend = max(feature_expr)+0.3) +
    ggplot2::geom_label(data = data.frame(1), inherit.aes = FALSE,
                        x = 1.5, y = max(feature_expr)+0.35,
                        label = significance,
                        fill = NA, label.size = 0) +
    ggplot2::lims(y = c(0, max(feature_expr)+0.4)) +
    # Theme
    ggplot2::theme(axis.title.x = element_blank(),
                   legend.position = "none")
  
  return(p)
})

names(plot_list) = features_oi
plot_list
## $DUSP1

## 
## $DDIT4

## 
## $GABARAP

## 
## $ZNF302

## 
## $MIF

## 
## $LGALS7

## 
## $ARF5

## 
## $S100A9

  • violin plots of DE genes in IFE basal, between HS and HD
subsobj = subset(sobj, cell_type == "IFE basal")
features_oi = c("DUSP1", "KLF6", "CLDN1", "CTGF",
                "IFI27", "IFITM3", "CCL2", "S100A9")

# Plot !
plot_list = lapply(features_oi, FUN = function(feature) {
  # t-test between sample type
  feature_expr = subsobj@assays$RNA@data[feature, ]
  feature_hs = feature_expr[subsobj$sample_type == "HS"]
  feature_hd = feature_expr[subsobj$sample_type == "HD"]
  feature_hs_VS_feature_hd = stats::t.test(feature_hs, feature_hd)
  feature_hs_VS_feature_hd
  
  # Significance associated with p-value
  pval = feature_hs_VS_feature_hd$p.value
  
  significance = case_when(pval > 0.05 ~ "ns",
                           pval > 0.01 & pval <= 0.05 ~ "*",
                           pval > 0.001 & pval <= 0.01 ~ "**",
                           pval <= 0.001 ~ "***")
  
  # Plot
  p = Seurat::VlnPlot(subsobj, group.by = "sample_type",
                      pt.size = 0.3, features = feature) +
    ggplot2::scale_fill_manual(values = sample_type_colors,
                               breaks = names(sample_type_colors)) +
    # Significance bar
    ggplot2::geom_segment(data = data.frame(1), inherit.aes = FALSE,
                          size = 0.5,
                          x = 1, xend = 2,
                          y = max(feature_expr)+0.3, yend = max(feature_expr)+0.3) +
    ggplot2::geom_label(data = data.frame(1), inherit.aes = FALSE,
                        x = 1.5, y = max(feature_expr)+0.35,
                        label = significance,
                        fill = NA, label.size = 0) +
    ggplot2::lims(y = c(0, max(feature_expr)+0.4)) +
    # Theme
    ggplot2::theme(axis.title.x = element_blank(),
                   legend.position = "none")
  
  return(p)
})

names(plot_list) = features_oi
plot_list
## $DUSP1

## 
## $KLF6

## 
## $CLDN1

## 
## $CTGF

## 
## $IFI27

## 
## $IFITM3

## 
## $CCL2

## 
## $S100A9

Wu dataset

We select the dataset:

sobj_atlas = list_data$wu_atlas$sobj
name2D_atlas = list_data$wu_atlas$name2D
sobj = list_data$wu_ors_ifeb$sobj
name2D = list_data$wu_ors_ifeb$name2D
list_results = list_data$wu_ors_ifeb$list_results

Location of cells on the whole atlas:

sobj_atlas$cell_bc = colnames(sobj_atlas)
sobj$cell_bc = colnames(sobj)
sobj_atlas$is_of_interest = dplyr::left_join(sobj_atlas@meta.data[, c("cell_bc", "percent.mt")],
                                             sobj@meta.data[, c("cell_bc", "cell_type")],
                                             by = "cell_bc")[, "cell_type"]

Seurat::DimPlot(sobj_atlas, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_of_interest", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(color_markers[cell_type_of_interest], bg_color),
                              breaks = c(cell_type_of_interest, NA), na.value = bg_color) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_blank()) +
  Seurat::NoAxes() + Seurat::NoLegend()

  • GSEA, enriched in ORS compared to IFE basal
the_gs_name = "REACTOME_KERATINIZATION" 
the_content = list_results$ORS_vs_IFE_basal$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      "\t|\tpvalue : ", round(the_content$pvalue, 4),
                      "\t|\tset size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$ORS_vs_IFE_basal$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

  • GSEA, enriched in IFE basal compared to ORS
the_gs_name = "HALLMARK_INTERFERON_GAMMA_RESPONSE" 
the_content = list_results$ORS_vs_IFE_basal$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      "\t|\tpvalue : ", round(the_content$pvalue, 4),
                      "\t|\tset size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$ORS_vs_IFE_basal$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

Takahashi dataset

We select the dataset:

sobj_atlas = list_data$takahashi_atlas$sobj
name2D_atlas = list_data$takahashi_atlas$name2D
sobj = list_data$takahashi_ors_ifeb$sobj
name2D = list_data$takahashi_ors_ifeb$name2D
list_results = list_data$takahashi_ors_ifeb$list_results

Location of cells on the whole atlas:

sobj_atlas$cell_bc = colnames(sobj_atlas)
sobj$cell_bc = colnames(sobj)
sobj_atlas$is_of_interest = dplyr::left_join(sobj_atlas@meta.data[, c("cell_bc", "percent.mt")],
                                             sobj@meta.data[, c("cell_bc", "cell_type")],
                                             by = "cell_bc")[, "cell_type"]

Seurat::DimPlot(sobj_atlas, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_of_interest", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(color_markers[cell_type_of_interest], bg_color),
                              breaks = c(cell_type_of_interest, NA), na.value = bg_color) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_blank()) +
  Seurat::NoAxes() + Seurat::NoLegend()

  • GSEA, enriched in ORS compared to IFE basal
the_gs_name = "REACTOME_KERATINIZATION" 
the_content = list_results$ORS_vs_IFE_basal$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      "\t|\tpvalue : ", round(the_content$pvalue, 4),
                      "\t|\tset size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$ORS_vs_IFE_basal$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

  • GSEA, enriched in IFE basal compared to ORS
the_gs_name = "HALLMARK_INTERFERON_GAMMA_RESPONSE" 
the_content = list_results$ORS_vs_IFE_basal$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      "\t|\tpvalue : ", round(the_content$pvalue, 4),
                      "\t|\tset size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$ORS_vs_IFE_basal$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

HF-SCs

We select the dataset:

sobj = list_data$hfsc$sobj
name2D = list_data$hfsc$name2D
list_results = list_data$hfsc$list_results
  • violin plots of DE genes in HF-SCs, between HS and HD
features_oi = c("HLA-C", "HLA-A", "IFITM3", "MIF", "JUNB", "PPIA",
                "PDK4", "DDIT4", "BTG2", "TXNIP", "KLF9")

# Plot !
plot_list = lapply(features_oi, FUN = function(feature) {
  # t-test between sample type
  feature_expr = sobj@assays$RNA@data[feature, ]
  feature_hs = feature_expr[sobj$sample_type == "HS"]
  feature_hd = feature_expr[sobj$sample_type == "HD"]
  feature_hs_VS_feature_hd = stats::t.test(feature_hs, feature_hd)
  feature_hs_VS_feature_hd
  
  # Significance associated with p-value
  pval = feature_hs_VS_feature_hd$p.value
  
  significance = case_when(pval > 0.05 ~ "ns",
                           pval > 0.01 & pval <= 0.05 ~ "*",
                           pval > 0.001 & pval <= 0.01 ~ "**",
                           pval <= 0.001 ~ "***")
  
  # Plot
  p = Seurat::VlnPlot(sobj, group.by = "sample_type",
                      pt.size = 0.3, features = feature) +
    ggplot2::scale_fill_manual(values = sample_type_colors,
                               breaks = names(sample_type_colors)) +
    # Significance bar
    ggplot2::geom_segment(data = data.frame(1), inherit.aes = FALSE,
                          size = 0.5,
                          x = 1, xend = 2,
                          y = max(feature_expr)+0.3, yend = max(feature_expr)+0.3) +
    ggplot2::geom_label(data = data.frame(1), inherit.aes = FALSE,
                        x = 1.5, y = max(feature_expr)+0.35,
                        label = significance,
                        fill = NA, label.size = 0) +
    ggplot2::lims(y = c(0, max(feature_expr)+0.4)) +
    # Theme
    ggplot2::theme(axis.title.x = element_blank(),
                   legend.position = "none")
  
  return(p)
})

names(plot_list) = features_oi
plot_list
## $`HLA-C`

## 
## $`HLA-A`

## 
## $IFITM3

## 
## $MIF

## 
## $JUNB

## 
## $PPIA

## 
## $PDK4

## 
## $DDIT4

## 
## $BTG2

## 
## $TXNIP

## 
## $KLF9

* heatmap

Heatmap between HS and HD :

# features_oi = list_results$HS_vs_HD %>%
#   dplyr::filter(pct.1 > 0.5 | pct.2 > 0.5) %>%
#   dplyr::arrange(-avg_logFC, -pct.1, -pct.2) %>%
#   rownames()
# features_oi = features_oi[!grepl(features_oi, pattern = "^RP")]

features_oi = c("IFITM3", "MIF", "HLA-C", "LMCD1", "TGFB2", "CYP1B1",
                "KRT6B", "WIF1", "HSPA1A", "COMT", "CISD1", "PRNP",  "CD81",
                "HLA-A", "B2M", "PPIA", "PYCARD", "CSAD", "PFN1", "HIF1A",
                "BASP1", "CAV1", "ZNF302", "LMO4", "SEPT11", "HTRA1",
                "GPM6A", "SOX4", "ZFP36L2", "RHOB", "CLDN1", "DUSP1", "TBX1",
                "ATF3", "DDIT4", "TXNIP", "BTG2", "SGK1", "KLF6", "LGR5")

# Matrix
mat_expr = Seurat::GetAssayData(sobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = cbind(mat_expr, sobj$percent.mt)
colnames(mat_expr)[ncol(mat_expr)] = "percent.rb"
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]   41 1454
## Colors
list_colors = list()

# Heatmap
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))

# Sample annotation (top annotation)
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
# list_colors[["seurat_clusters"]] = setNames(aquarius::gg_color_hue(length(levels(sobj$seurat_clusters))),
#                                             nm = levels(sobj$seurat_clusters))
# Cells order
column_order = sobj@meta.data %>%
  dplyr::arrange(sample_type, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(sobj@meta.data))

# Heatmap
ha_top = HeatmapAnnotation(sample_type = sobj$sample_type,
                           sample_identifier = sobj$sample_identifier,
                           # clusters = sobj$seurat_clusters,
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]]
                                      # clusters = list_colors[["seurat_clusters"]]
                           ))


# g1 : REACTOME_CYTOKINE_SIGNALING_IN_IMMUNE_SYSTEM
# g2 : GOBP_APOPTOTIC_PROCESS
g1_genes = c("B2M", "HLA-C", "HLA-A", "MIF", "PPIA", "JUNB", "IFITM3")
g2_genes = c("JUN", "ATF3", "BTG2", "RHOB", "NFKBIA", "SGK1", "KLF9",
             "CAV1", "DDIT4", "PDK4", "TXNIP", "RNF1152", "TLE1")
ha_right = data.frame(genes =  c(features_oi, "percent.rb"), rownames = c(features_oi, "percent.rb"))
ha_right$group = case_when(ha_right$genes %in% g1_genes ~ "REACTOME_CYTOKINE_SIGNALING_IN_IMMUNE_SYSTEM",
                           ha_right$genes %in% g2_genes ~ "GOBP_APOPTOTIC_PROCESS",
                           TRUE ~ "others")

list_colors[["group"]] = setNames(
  nm = c("REACTOME_CYTOKINE_SIGNALING_IN_IMMUNE_SYSTEM", "GOBP_APOPTOTIC_PROCESS", "others"),
  c("red", "black", "gray90"))

ha_right = HeatmapAnnotation(group = ha_right$group,
                             col = list(group = list_colors[["group"]]),
                             which = "row",
                             show_annotation_name = FALSE,
                             show_legend = TRUE)

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             right_annotation = ha_right,
             show_column_names = FALSE,
             column_order = column_order,
             column_gap = unit(2, "mm"),
             cluster_rows = FALSE,
             show_row_dend = FALSE,
             row_title = NULL,
             row_names_gp = grid::gpar(fontsize = 10, fontface = "plain"),
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom",
                     annotation_legend_side = "bottom")

Immune cells

We select the datasets:

sobj_atlas = list_data$our_atlas$sobj
name2D_atlas = list_data$our_atlas$name2D
sobj = list_data$immune$sobj
name2D = list_data$immune$name2D

cell_type_of_interest = c("CD4 T cells", "CD8 T cells", "Langerhans cells",
                          "macrophages", "B cells", "proliferative")

Location of cells on the whole atlas:

sobj_atlas$cell_bc = colnames(sobj_atlas)
sobj$cell_bc = colnames(sobj)
sobj_atlas$is_of_interest = dplyr::left_join(sobj_atlas@meta.data[, c("cell_bc", "percent.mt")],
                                             sobj@meta.data[, c("cell_bc", "cluster_type")],
                                             by = "cell_bc")[, "cluster_type"]

Seurat::DimPlot(sobj_atlas, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_of_interest", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(color_markers[cell_type_of_interest], bg_color),
                              breaks = c(cell_type_of_interest, NA), na.value = bg_color) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_blank()) +
  Seurat::NoAxes() + Seurat::NoLegend()

  • cell type annotation
Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

  • cluster type annotation
Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

  • feature plots for genes related to TCR alpha
features_oi = c("CD3E", "CD4", "CD8A",
                sort(grep(rownames(sobj), pattern = "^TRA[C|V]", value = TRUE)))

plot_list = lapply(features_oi, FUN = function(one_gene) {
  p = Seurat::FeaturePlot(sobj, features = one_gene,
                          reduction = name2D, order = TRUE) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    ggplot2::theme(aspect.ratio = 1) +
    Seurat::NoAxes() +
    Seurat::NoLegend()
  
  return(p)
})

patchwork::wrap_plots(plot_list, nrow = 4)

  • feature plots split by sample type (HS / HD)
features_oi = data.frame(population = c("all immune cells", rep("macrophages", 2),
                                      rep("CD4 T cells", 3), rep("CD8 T cells", 2)),
                         feature = c("IL6", "IL1B", "TNF", "GZMA", "IFNG", "IL17A", "PRF1", "GZMB"))
features_oi
##         population feature
## 1 all immune cells     IL6
## 2      macrophages    IL1B
## 3      macrophages     TNF
## 4      CD4 T cells    GZMA
## 5      CD4 T cells    IFNG
## 6      CD4 T cells   IL17A
## 7      CD8 T cells    PRF1
## 8      CD8 T cells    GZMB
patchwork_list = lapply(features_oi$feature, FUN = function(one_gene) {
  plot_list = aquarius::plot_split_dimred(sobj,
                                          reduction = name2D,
                                          split_by = "sample_type",
                                          color_by = one_gene,
                                          order = TRUE,
                                          bg_color = bg_color)
  
  p = patchwork::wrap_plots(plot_list, nrow = 1) +
    patchwork::plot_layout(guides = "collect")
  
  return(p)
})
names(patchwork_list) = features_oi$feature

patchwork_list
## $IL6

## 
## $IL1B

## 
## $TNF

## 
## $GZMA

## 
## $IFNG

## 
## $IL17A

## 
## $PRF1

## 
## $GZMB

  • violin plots within a specific population
plot_list = lapply(c(1:nrow(features_oi)), FUN = function(i) {
  one_gene = features_oi$feature[i]
  population = features_oi$population[i]
  
  # Subset object
  if (population != "all immune cells") {
    subsobj = subset(sobj, cluster_type %in% population)
  } else {
   subsobj = sobj 
  }
  
  # t-test between sample type
  feature_expr = subsobj@assays$RNA@data[one_gene, ]
  feature_hs = feature_expr[subsobj$sample_type == "HS"]
  feature_hd = feature_expr[subsobj$sample_type == "HD"]
  feature_hs_VS_feature_hd = stats::t.test(feature_hs, feature_hd)
  feature_hs_VS_feature_hd
  
  # Significance associated with p-value
  pval = feature_hs_VS_feature_hd$p.value
  
  significance = case_when(pval > 0.05 ~ "ns",
                           pval > 0.01 & pval <= 0.05 ~ "*",
                           pval > 0.001 & pval <= 0.01 ~ "**",
                           pval <= 0.001 ~ "***")
  
  # Plot
  p = Seurat::VlnPlot(subsobj, group.by = "sample_type",
                      pt.size = 0.3, features = one_gene) +
    ggplot2::scale_fill_manual(values = sample_type_colors,
                               breaks = names(sample_type_colors)) +
    ggplot2::labs(title = population,
                  subtitle = one_gene) +
    # Significance bar
    ggplot2::geom_segment(data = data.frame(1), inherit.aes = FALSE,
                          size = 0.5,
                          x = 1, xend = 2,
                          y = max(feature_expr)+0.3, yend = max(feature_expr)+0.3) +
    ggplot2::geom_label(data = data.frame(1), inherit.aes = FALSE,
                        x = 1.5, y = max(feature_expr)+0.35,
                        label = significance,
                        fill = NA, label.size = 0) +
    ggplot2::lims(y = c(0, max(feature_expr)+0.4)) +
    # Theme
    ggplot2::theme(axis.title.x = element_blank(),
                   plot.title = element_text(size = 10),
                   plot.subtitle = element_text(hjust = 0.5),
                   legend.position = "none")
  
  return(p)
})
names(plot_list) = features_oi$feature

plot_list
## $IL6

## 
## $IL1B

## 
## $TNF

## 
## $GZMA

## 
## $IFNG

## 
## $IL17A

## 
## $PRF1

## 
## $GZMB

  • heatmap macrophages
subsobj = subset(sobj, cluster_type == "macrophages")
features_oi = c("IL1B", "TNF",
                "HLA-DQA2", "HLA-DPA1", "HLA-DRB5",
                "HLA-A", "HLA-C", "B2M",
                "C1QA", "C1QB", "C1QC")

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]  11 463
## Colors
list_colors = list()

# Heatmap
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))

# Sample annotation (top annotation)
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
# Cells order
column_order = subsobj@meta.data %>%
  dplyr::arrange(sample_type, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Heatmap
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]]))

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             show_column_names = FALSE,
             column_order = column_order,
             column_gap = unit(2, "mm"),
             cluster_rows = FALSE,
             row_title = NULL,
             row_names_gp = grid::gpar(fontsize = 14, fontface = "plain"),
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom",
                     annotation_legend_side = "bottom")

  • heatmap T cells
subsobj = subset(sobj, cluster_type == "CD4 T cells")
features_oi = c("GZMA", "KLRB1", "BTG1", "ZFP36", "NFKBIA", "TXNIP", "CXCR4", "IFNG", "IL17A")

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]   9 848
## Colors
list_colors = list()

# Heatmap
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))

# Sample annotation (top annotation)
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
# Cells order
column_order = subsobj@meta.data %>%
  dplyr::arrange(sample_type, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Heatmap
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]]))

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             show_column_names = FALSE,
             column_order = column_order,
             column_gap = unit(2, "mm"),
             cluster_rows = FALSE,
             row_title = NULL,
             row_names_gp = grid::gpar(fontsize = 14, fontface = "plain"),
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "left",
                     annotation_legend_side = "left")

Supplementary tables

In this section, we save files to associate with the manuscript, as supplementary tables.

Table S2 (package version)

We load the table :

package_version = read.table(paste0(".", "/data/info_to_install_2023_04_17.txt"),
                             header = TRUE)
head(package_version)
##   order package_name  version
## 1     1      acepack    1.4.1
## 2     2    ADGofTest      0.3
## 3     3    backports    1.2.1
## 4     4    base64enc    0.1-3
## 5     5           BH 1.72.0-3
## 6     6          bit    4.0.4
##                                                                              url
## 1                    https://cran.r-project.org/src/contrib/acepack_1.4.1.tar.gz
## 2                    https://cran.r-project.org/src/contrib/ADGofTest_0.3.tar.gz
## 3 http://cran.r-project.org/src/contrib/Archive/backports/backports_1.2.1.tar.gz
## 4                  https://cran.r-project.org/src/contrib/base64enc_0.1-3.tar.gz
## 5            http://cran.r-project.org/src/contrib/Archive/BH/BH_1.72.0-3.tar.gz
## 6             http://cran.r-project.org/src/contrib/Archive/bit/bit_4.0.4.tar.gz
##           type
## 1    CRAN_last
## 2    CRAN_last
## 3 CRAN_archive
## 4    CRAN_last
## 5 CRAN_archive
## 6 CRAN_archive

Columns correspond to :

  • order : order to install package such that one never need to install dependencies
  • package_name : package name
  • version : installed version, on the local machine
  • url : the package was installed in the Singularity container using this url

We save this table, except the last column (just for me) :

openxlsx::write.xlsx(package_version[, c(1:4)],
                     file = paste0(".", "/data/Supplementary Table 2.xlsx"))

Table S3 (cell type annotation)

We load the table :

cell_markers = readRDS(paste0(data_dir, "/1_metadata/hs_hd_cell_markers.rds"))
lengths(cell_markers)
##          CD4 T cells          CD8 T cells     Langerhans cells 
##                   13                   13                    9 
##          macrophages              B cells              cuticle 
##                   10                   16                   15 
##               cortex              medulla                  IRS 
##                   16                   10                   16 
##        proliferative               HF-SCs            IFE basal 
##                   20                   17                   16 
## IFE granular spinous                  ORS          melanocytes 
##                   17                   15                   10 
##            sebocytes 
##                    8

We save this table, except the last column (just for me) :

openxlsx::write.xlsx(cell_markers,
                     file = paste0(".", "/data/Supplementary Table 3.xlsx"))

R Session

show
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/local/lib/R/lib/libRblas.so
## LAPACK: /usr/local/lib/R/lib/libRlapack.so
## 
## locale:
## [1] C
## 
## attached base packages:
## [1] grid      stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
## [1] ComplexHeatmap_2.14.0 ggplot2_3.3.5         patchwork_1.1.2      
## [4] dplyr_1.0.7          
## 
## loaded via a namespace (and not attached):
##   [1] softImpute_1.4              graphlayouts_0.7.0         
##   [3] pbapply_1.4-2               lattice_0.20-41            
##   [5] haven_2.3.1                 dyndimred_1.0.3            
##   [7] vctrs_0.3.8                 usethis_2.0.1              
##   [9] dynwrap_1.2.1               blob_1.2.1                 
##  [11] survival_3.2-13             prodlim_2019.11.13         
##  [13] dynutils_1.0.5              later_1.3.0                
##  [15] DBI_1.1.1                   R.utils_2.11.0             
##  [17] SingleCellExperiment_1.8.0  rappdirs_0.3.3             
##  [19] uwot_0.1.8                  dqrng_0.2.1                
##  [21] jpeg_0.1-8.1                zlibbioc_1.32.0            
##  [23] pspline_1.0-18              pcaMethods_1.78.0          
##  [25] mvtnorm_1.1-1               htmlwidgets_1.5.4          
##  [27] GlobalOptions_0.1.2         future_1.22.1              
##  [29] UpSetR_1.4.0                laeken_0.5.2               
##  [31] leiden_0.3.3                clustree_0.4.3             
##  [33] lmds_0.1.0                  parallel_3.6.3             
##  [35] scater_1.14.6               irlba_2.3.3                
##  [37] markdown_1.1                DEoptimR_1.0-9             
##  [39] tidygraph_1.1.2             Rcpp_1.0.9                 
##  [41] readr_2.0.2                 KernSmooth_2.23-17         
##  [43] carrier_0.1.0               promises_1.1.0             
##  [45] gdata_2.18.0                DelayedArray_0.12.3        
##  [47] limma_3.42.2                graph_1.64.0               
##  [49] RcppParallel_5.1.4          Hmisc_4.4-0                
##  [51] fs_1.5.2                    RSpectra_0.16-0            
##  [53] fastmatch_1.1-0             ranger_0.12.1              
##  [55] digest_0.6.25               png_0.1-7                  
##  [57] sctransform_0.2.1           cowplot_1.0.0              
##  [59] DOSE_3.12.0                 here_1.0.1                 
##  [61] TInGa_0.0.0.9000            dynplot_1.1.0              
##  [63] ggraph_2.0.3                pkgconfig_2.0.3            
##  [65] GO.db_3.10.0                DelayedMatrixStats_1.8.0   
##  [67] gower_0.2.1                 ggbeeswarm_0.6.0           
##  [69] iterators_1.0.12            DropletUtils_1.6.1         
##  [71] reticulate_1.26             clusterProfiler_3.14.3     
##  [73] SummarizedExperiment_1.16.1 circlize_0.4.15            
##  [75] beeswarm_0.4.0              GetoptLong_1.0.5           
##  [77] xfun_0.35                   bslib_0.3.1                
##  [79] zoo_1.8-10                  tidyselect_1.1.0           
##  [81] GA_3.2                      reshape2_1.4.4             
##  [83] purrr_0.3.4                 ica_1.0-2                  
##  [85] pcaPP_1.9-73                viridisLite_0.3.0          
##  [87] rtracklayer_1.46.0          rlang_1.0.2                
##  [89] hexbin_1.28.1               jquerylib_0.1.4            
##  [91] dyneval_0.9.9               glue_1.4.2                 
##  [93] waldo_0.3.1                 RColorBrewer_1.1-2         
##  [95] matrixStats_0.56.0          stringr_1.4.0              
##  [97] lava_1.6.7                  europepmc_0.3              
##  [99] DESeq2_1.26.0               recipes_0.1.17             
## [101] labeling_0.3                httpuv_1.5.2               
## [103] class_7.3-17                BiocNeighbors_1.4.2        
## [105] DO.db_2.9                   annotate_1.64.0            
## [107] jsonlite_1.7.2              XVector_0.26.0             
## [109] bit_4.0.4                   mime_0.9                   
## [111] aquarius_0.1.5              Rsamtools_2.2.3            
## [113] gridExtra_2.3               gplots_3.0.3               
## [115] stringi_1.4.6               processx_3.5.2             
## [117] gsl_2.1-6                   bitops_1.0-6               
## [119] cli_3.0.1                   batchelor_1.2.4            
## [121] RSQLite_2.2.0               randomForest_4.6-14        
## [123] tidyr_1.1.4                 data.table_1.14.2          
## [125] rstudioapi_0.13             org.Mm.eg.db_3.10.0        
## [127] GenomicAlignments_1.22.1    nlme_3.1-147               
## [129] qvalue_2.18.0               scran_1.14.6               
## [131] locfit_1.5-9.4              scDblFinder_1.1.8          
## [133] listenv_0.8.0               ggthemes_4.2.4             
## [135] gridGraphics_0.5-0          R.oo_1.24.0                
## [137] dbplyr_1.4.4                BiocGenerics_0.32.0        
## [139] TTR_0.24.2                  readxl_1.3.1               
## [141] lifecycle_1.0.1             timeDate_3043.102          
## [143] ggpattern_0.3.1             munsell_0.5.0              
## [145] cellranger_1.1.0            R.methodsS3_1.8.1          
## [147] proxyC_0.1.5                visNetwork_2.0.9           
## [149] caTools_1.18.0              codetools_0.2-16           
## [151] Biobase_2.46.0              GenomeInfoDb_1.22.1        
## [153] vipor_0.4.5                 lmtest_0.9-38              
## [155] msigdbr_7.5.1               htmlTable_1.13.3           
## [157] triebeard_0.3.0             lsei_1.2-0                 
## [159] xtable_1.8-4                ROCR_1.0-7                 
## [161] BiocManager_1.30.10         scatterplot3d_0.3-41       
## [163] abind_1.4-5                 farver_2.0.3               
## [165] parallelly_1.28.1           RANN_2.6.1                 
## [167] askpass_1.1                 GenomicRanges_1.38.0       
## [169] RcppAnnoy_0.0.16            tibble_3.1.5               
## [171] ggdendro_0.1-20             cluster_2.1.0              
## [173] future.apply_1.5.0          Seurat_3.1.5               
## [175] dendextend_1.15.1           Matrix_1.3-2               
## [177] ellipsis_0.3.2              prettyunits_1.1.1          
## [179] lubridate_1.7.9             ggridges_0.5.2             
## [181] igraph_1.2.5                RcppEigen_0.3.3.7.0        
## [183] fgsea_1.12.0                remotes_2.4.2              
## [185] scBFA_1.0.0                 destiny_3.0.1              
## [187] VIM_6.1.1                   testthat_3.1.0             
## [189] htmltools_0.5.2             BiocFileCache_1.10.2       
## [191] yaml_2.2.1                  utf8_1.1.4                 
## [193] plotly_4.9.2.1              XML_3.99-0.3               
## [195] ModelMetrics_1.2.2.2        e1071_1.7-3                
## [197] foreign_0.8-76              withr_2.5.0                
## [199] fitdistrplus_1.0-14         BiocParallel_1.20.1        
## [201] xgboost_1.4.1.1             bit64_4.0.5                
## [203] foreach_1.5.0               robustbase_0.93-9          
## [205] Biostrings_2.54.0           GOSemSim_2.13.1            
## [207] rsvd_1.0.3                  memoise_2.0.0              
## [209] evaluate_0.18               forcats_0.5.0              
## [211] rio_0.5.16                  geneplotter_1.64.0         
## [213] tzdb_0.1.2                  caret_6.0-86               
## [215] ps_1.6.0                    DiagrammeR_1.0.6.1         
## [217] curl_4.3                    fdrtool_1.2.15             
## [219] fansi_0.4.1                 highr_0.8                  
## [221] urltools_1.7.3              xts_0.12.1                 
## [223] GSEABase_1.48.0             acepack_1.4.1              
## [225] edgeR_3.28.1                checkmate_2.0.0            
## [227] scds_1.2.0                  cachem_1.0.6               
## [229] npsurv_0.4-0                babelgene_22.3             
## [231] rjson_0.2.20                openxlsx_4.1.5             
## [233] ggrepel_0.9.1               clue_0.3-60                
## [235] rprojroot_2.0.2             stabledist_0.7-1           
## [237] tools_3.6.3                 sass_0.4.0                 
## [239] nichenetr_1.1.1             magrittr_2.0.1             
## [241] RCurl_1.98-1.2              proxy_0.4-24               
## [243] car_3.0-11                  ape_5.3                    
## [245] ggplotify_0.0.5             xml2_1.3.2                 
## [247] httr_1.4.2                  assertthat_0.2.1           
## [249] rmarkdown_2.18              boot_1.3-25                
## [251] globals_0.14.0              R6_2.4.1                   
## [253] Rhdf5lib_1.8.0              nnet_7.3-14                
## [255] RcppHNSW_0.2.0              progress_1.2.2             
## [257] genefilter_1.68.0           statmod_1.4.34             
## [259] gtools_3.8.2                shape_1.4.6                
## [261] HDF5Array_1.14.4            BiocSingular_1.2.2         
## [263] rhdf5_2.30.1                splines_3.6.3              
## [265] AUCell_1.8.0                carData_3.0-4              
## [267] colorspace_1.4-1            generics_0.1.0             
## [269] stats4_3.6.3                base64enc_0.1-3            
## [271] dynfeature_1.0.0            smoother_1.1               
## [273] gridtext_0.1.1              pillar_1.6.3               
## [275] tweenr_1.0.1                sp_1.4-1                   
## [277] ggplot.multistats_1.0.0     rvcheck_0.1.8              
## [279] GenomeInfoDbData_1.2.2      plyr_1.8.6                 
## [281] gtable_0.3.0                zip_2.2.0                  
## [283] knitr_1.41                  latticeExtra_0.6-29        
## [285] biomaRt_2.42.1              IRanges_2.20.2             
## [287] fastmap_1.1.0               ADGofTest_0.3              
## [289] copula_1.0-0                doParallel_1.0.15          
## [291] AnnotationDbi_1.48.0        vcd_1.4-8                  
## [293] babelwhale_1.0.1            openssl_1.4.1              
## [295] scales_1.1.1                backports_1.2.1            
## [297] S4Vectors_0.24.4            ipred_0.9-12               
## [299] enrichplot_1.6.1            hms_1.1.1                  
## [301] ggforce_0.3.1               Rtsne_0.15                 
## [303] shiny_1.7.1                 numDeriv_2016.8-1.1        
## [305] polyclip_1.10-0             lazyeval_0.2.2             
## [307] Formula_1.2-3               tsne_0.1-3                 
## [309] crayon_1.3.4                MASS_7.3-54                
## [311] pROC_1.16.2                 viridis_0.5.1              
## [313] dynparam_1.0.0              rpart_4.1-15               
## [315] zinbwave_1.8.0              compiler_3.6.3             
## [317] ggtext_0.1.0
LS0tCnRpdGxlOiAiSFMgcHJvamVjdCIKc3VidGl0bGU6ICJGaWd1cmVzIgphdXRob3I6ICJBdWRyZXkiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVZLSVtLSVkJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKLS0tCgo8c3R5bGU+CmJvZHkgewp0ZXh0LWFsaWduOiBqdXN0aWZ5fQo8L3N0eWxlPgoKPCEtLSBBdXRvbWF0aWNhbGx5IGNvbXB1dGVzIGFuZCBwcmludHMgaW4gdGhlIG91dHB1dCB0aGUgcnVubmluZyB0aW1lIGZvciBhbnkgY29kZSBjaHVuayAtLT4KYGBge3IsIGVjaG89RkFMU0V9CiMgaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vcm1hcmtkb3duL2lzc3Vlcy8xNDUzCmhvb2tzID0ga25pdHI6OmtuaXRfaG9va3MkZ2V0KCkKaG9va19mb2xkYWJsZSA9IGZ1bmN0aW9uKHR5cGUpIHsKICBmb3JjZSh0eXBlKQogIGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgIHJlcyA9IGhvb2tzW1t0eXBlXV0oeCwgb3B0aW9ucykKICAgIAogICAgaWYgKGlzRkFMU0Uob3B0aW9uc1tbcGFzdGUwKCJmb2xkXyIsIHR5cGUpXV0pKSByZXR1cm4ocmVzKQogICAgCiAgICBwYXN0ZTAoCiAgICAgICI8ZGV0YWlscz48c3VtbWFyeT4iLCAic2hvdyIsICI8L3N1bW1hcnk+XG5cbiIsCiAgICAgIHJlcywKICAgICAgIlxuXG48L2RldGFpbHM+IgogICAgKQogIH0KfQprbml0cjo6a25pdF9ob29rcyRzZXQoCiAgb3V0cHV0ID0gaG9va19mb2xkYWJsZSgib3V0cHV0IiksCiAgcGxvdCA9IGhvb2tfZm9sZGFibGUoInBsb3QiKSwKICB0aW1lX2l0ID0gbG9jYWwoewogICAgbm93ID0gTlVMTAogICAgZnVuY3Rpb24oYmVmb3JlLCBvcHRpb25zKSB7CiAgICAgIGlmIChvcHRpb25zJHRpbWVfaXQpIHsKICAgICAgICBpZiAoYmVmb3JlKSB7CiAgICAgICAgICBub3cgPD0gU3lzLnRpbWUoKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICByZXMgPSBkaWZmdGltZShTeXMudGltZSgpLCBub3csIHVuaXRzID0gInNlY3MiKQogICAgICAgICAgcGFzdGUoIihUaW1lIHRvIHJ1biA6Iiwgcm91bmQocmVzLCBkaWdpdHMgPSAyKSwgInMpIikKICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9KQopCmBgYAoKPCEtLSBTZXQgZGVmYXVsdCBwYXJhbWV0ZXJzIGZvciBhbGwgY2h1bmtzIC0tPgpgYGB7ciwgc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Kc2V0LnNlZWQoMTMzN0wpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgIyBkaXNwbGF5IGNvZGUKICAgICAgICAgICAgICAgICAgICAgICMgZGlzcGxheSBjaHVuayBvdXRwdXQKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZvbGRfb3V0cHV0ID0gRkFMU0UsICMgdXNlZnVsIGZvciBzZXNzaW9uSW5mbygpCiAgICAgICAgICAgICAgICAgICAgICBmb2xkX3Bsb3QgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBmaWd1cmUgc2V0dGluZ3MKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICdjZW50ZXInLAogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gMjAsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gMTUsCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgc29tZXRoaW5nIGFib3V0IHNlZWQsIGNodW5rIGFuZCBSbWFya2Rvd24gY29tcGlsYXRpb24KICAgICAgICAgICAgICAgICAgICAgICMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzk0MTcwMDMvbG9uZy12ZWN0b3JzLW5vdC1zdXBwb3J0ZWQteWV0LWVycm9yLWluLXJtZC1idXQtbm90LWluLXItc2NyaXB0CiAgICAgICAgICAgICAgICAgICAgICAjIGNhY2hlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGNhY2hlLmxhenkgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgYWRkIHJ1bnRpbWUgYWZ0ZXIgY2h1bmsKICAgICAgICAgICAgICAgICAgICAgIHRpbWVfaXQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBzYXZlIGZpZ3VyZXMgaW4gUERGIGluIGEgc2VwYXJhdGUgZm9sZGVyCiAgICAgICAgICAgICAgICAgICAgICBkZXYgPSBjKCdwbmcnLCAncGRmJyksICMgdGlmZiBvciBwZGYgYWxvbmUgcmVuZGVycyBiYWQgaW4gaHRtbAogICAgICAgICAgICAgICAgICAgICAgIyBkcGkgPSAzMDAsCiAgICAgICAgICAgICAgICAgICAgICBmaWcucGF0aCA9ICJmaWd1cmVzX2RldGFpbC8iLAogICAgICAgICAgICAgICAgICAgICAgcGRmLm9wdGlvbnMoZW5jb2RpbmcgPSAiSVNPTGF0aW45LmVuYyIpKQpgYGAKCgpUaGlzIGZpbGUgaXMgdXNlZCB0byBwcmVwYXJlIHRoZSBmaWd1cmVzIGZvciB0aGUgcGFwZXIuCgpgYGB7ciBsaWJyYXJ5fQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQoKLmxpYlBhdGhzKCkKYGBgCgoKIyBQcmVwYXJhdGlvbgoKSGVyZSBhcmUgdGhlIGZvbGRlcnMgd2hlcmUgYW5hbHlzZXMgYXJlIHN0b3JlZCA6CgpgYGB7ciBsb2NhdGlvbnN9CmRhdGFfZGlyID0gIi4vLi4iCmxpc3QuZmlsZXMoZGF0YV9kaXIpCmBgYAoKVGhlc2UgYXJlIHRoZSBjdXN0b20gY29sb3JzIGZvciBjZWxsIHBvcHVsYXRpb25zIDoKCmBgYHtyIGNvbG9yX21hcmtlcnMsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMS4yfQpjb2xvcl9tYXJrZXJzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvMV9tZXRhZGF0YS9oc19oZF9jb2xvcl9tYXJrZXJzLnJkcyIpKQpjb2xvcl9tYXJrZXJzID0gY29sb3JfbWFya2Vyc1tuYW1lcyhjb2xvcl9tYXJrZXJzKSAhPSAibWVsYW5vY3l0ZXMiXSAjIHJlbW92ZSBtZWxhbm9jeXRlcwoKZGF0YS5mcmFtZShjZWxsX3R5cGUgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSwKICAgICAgICAgICBjb2xvciA9IHVubGlzdChjb2xvcl9tYXJrZXJzKSkgJT4lCiAgZ2dwbG90Mjo6Z2dwbG90KC4sIGFlcyh4ID0gY2VsbF90eXBlLCB5ID0gMCwgZmlsbCA9IGNlbGxfdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHBjaCA9IDIxLCBzaXplID0gNSkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHVubGlzdChjb2xvcl9tYXJrZXJzKSwgYnJlYWtzID0gbmFtZXMoY29sb3JfbWFya2VycykpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzAsIGhqdXN0ID0gMSkpCmBgYAoKV2UgZGVmaW5lIGN1c3RvbSBjb2xvcnMgZm9yIHNhbXBsZSB0eXBlIDoKCmBgYHtyIHNhbXBsZV90eXBlX2NvbG9ycywgZmlnLndpZHRoID0gMywgZmlnLmhlaWdodCA9IDAuNzV9CnNhbXBsZV90eXBlX2NvbG9ycyA9IHNldE5hbWVzKG5tID0gYygiSFMiLCAiSEQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiI0M1NUY0MCIsICIjMkM3OEU2IikpCgpkYXRhLmZyYW1lKGNlbGxfdHlwZSA9IG5hbWVzKHNhbXBsZV90eXBlX2NvbG9ycyksCiAgICAgICAgICAgY29sb3IgPSB1bmxpc3Qoc2FtcGxlX3R5cGVfY29sb3JzKSkgJT4lCiAgZ2dwbG90Mjo6Z2dwbG90KC4sIGFlcyh4ID0gY2VsbF90eXBlLCB5ID0gMCwgZmlsbCA9IGNlbGxfdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHBjaCA9IDIxLCBzaXplID0gNSkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHVubGlzdChzYW1wbGVfdHlwZV9jb2xvcnMpLCBicmVha3MgPSBuYW1lcyhzYW1wbGVfdHlwZV9jb2xvcnMpKSArCiAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsKICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpXZSBkZWZpbmUgY3VzdG9tIGNvbG9ycyBmb3IgbGFib3JhdG9yeSA6CgpgYGB7ciBsYWJvcmF0b3J5X2NvbG9ycywgZmlnLndpZHRoID0gMywgZmlnLmhlaWdodCA9IDAuNzV9CmxhYm9yYXRvcnlfY29sb3JzID0gc2V0TmFtZXMobm0gPSBjKCJPdXIiLCAiV3UiLCAiVGFrYWhhc2hpIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiZmlyZWJyaWNrMyIsICJkZWVwc2t5Ymx1ZSIsICJtZWRpdW1wdXJwbGUiKSkKCmRhdGEuZnJhbWUoY2VsbF90eXBlID0gbmFtZXMobGFib3JhdG9yeV9jb2xvcnMpLAogICAgICAgICAgIGNvbG9yID0gdW5saXN0KGxhYm9yYXRvcnlfY29sb3JzKSkgJT4lCiAgZ2dwbG90Mjo6Z2dwbG90KC4sIGFlcyh4ID0gY2VsbF90eXBlLCB5ID0gMCwgZmlsbCA9IGNlbGxfdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHBjaCA9IDIxLCBzaXplID0gNSkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHVubGlzdChsYWJvcmF0b3J5X2NvbG9ycyksIGJyZWFrcyA9IG5hbWVzKGxhYm9yYXRvcnlfY29sb3JzKSkgKwogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKV2UgZGVmaW5lIGN1c3RvbSBjb2xvcnMgZm9yIGxvY2F0aW9uIDoKCmBgYHtyIGxvY2F0aW9uX2NvbG9ycywgZmlnLndpZHRoID0gMywgZmlnLmhlaWdodCA9IDAuNzV9CmxvY2F0aW9uX2NvbG9ycyA9IHNldE5hbWVzKG5tID0gYygiYXhpbGxhcnkiLCAiaGFpciBzY2FscCIsICJwdWJpcyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJmb3Jlc3RncmVlbiIsICJvcmNoaWQzIiwgImRhcmtvcmFuZ2UxIikpCgpkYXRhLmZyYW1lKGNlbGxfdHlwZSA9IG5hbWVzKGxvY2F0aW9uX2NvbG9ycyksCiAgICAgICAgICAgY29sb3IgPSB1bmxpc3QobG9jYXRpb25fY29sb3JzKSkgJT4lCiAgZ2dwbG90Mjo6Z2dwbG90KC4sIGFlcyh4ID0gY2VsbF90eXBlLCB5ID0gMCwgZmlsbCA9IGNlbGxfdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHBjaCA9IDIxLCBzaXplID0gNSkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHVubGlzdChsb2NhdGlvbl9jb2xvcnMpLCBicmVha3MgPSBuYW1lcyhsb2NhdGlvbl9jb2xvcnMpKSArCiAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsKICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpXZSBkZWZpbmUgY3VzdG9tIGNvbG9ycyBmb3IgZ2VuZGVyIDoKCmBgYHtyIGdlbmRlcl9jb2xvcnMsIGZpZy53aWR0aCA9IDMsIGZpZy5oZWlnaHQgPSAwLjc1fQpnZW5kZXJfY29sb3JzID0gc2V0TmFtZXMobm0gPSBjKCJGIiwgIk0iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYygibGlnaHRzbGF0ZWJsdWUiLCAiY2hhcnRyZXVzZTMiKSkKCmRhdGEuZnJhbWUoY2VsbF90eXBlID0gbmFtZXMoZ2VuZGVyX2NvbG9ycyksCiAgICAgICAgICAgY29sb3IgPSB1bmxpc3QoZ2VuZGVyX2NvbG9ycykpICU+JQogIGdncGxvdDI6OmdncGxvdCguLCBhZXMoeCA9IGNlbGxfdHlwZSwgeSA9IDAsIGZpbGwgPSBjZWxsX3R5cGUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChwY2ggPSAyMSwgc2l6ZSA9IDUpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB1bmxpc3QoZ2VuZGVyX2NvbG9ycyksIGJyZWFrcyA9IG5hbWVzKGdlbmRlcl9jb2xvcnMpKSArCiAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsKICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpXZSBzZXQgYSBiYWNrZ3JvdW5kIGNvbG9yIDoKCmBgYHtyIGJnX2NvbG9yfQpiZ19jb2xvciA9ICJncmF5OTQiCmBgYAoKClRoaXMgaXMgdGhlIGNvcnJlc3BvbmRlbmNlIGJldHdlZW4gY2VsbCB0eXBlcyBhbmQgY2VsbCBmYW1pbGllcywgYW5kIGN1c3RvbSBjb2xvcnMgdG8gY29sb3IgY2VsbHMgYnkgY2VsbCBjYXRlZ29yeSA6CgpgYGB7ciBjZWxsX2NhdGVnb3J5fQpjdXN0b21fb3JkZXJfY2VsbF90eXBlID0gZGF0YS5mcmFtZSgKICBjZWxsX3R5cGUgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSwKICBjZWxsX2NhdGVnb3J5ID0gYyhyZXAoImltbXVuZSBjZWxscyIsIDUpLAogICAgICAgICAgICAgICAgICAgIHJlcCgibWF0cml4IiwgNSksCiAgICAgICAgICAgICAgICAgICAgcmVwKCJub24gbWF0cml4IiwgNSkpLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKY3VzdG9tX29yZGVyX2NlbGxfdHlwZSRjZWxsX3R5cGUgPSBmYWN0b3IoY3VzdG9tX29yZGVyX2NlbGxfdHlwZSRjZWxsX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGN1c3RvbV9vcmRlcl9jZWxsX3R5cGUkY2VsbF90eXBlKQpyb3duYW1lcyhjdXN0b21fb3JkZXJfY2VsbF90eXBlKSA9IGN1c3RvbV9vcmRlcl9jZWxsX3R5cGUkY2VsbF90eXBlCmN1c3RvbV9vcmRlcl9jZWxsX3R5cGUKCmNhdGVnb3J5X2NvbG9yID0gYygiaW1tdW5lIGNlbGxzIiA9ICJzbGF0ZWJsdWUxIiwKICAgICAgICAgICAgICAgICAgICJtYXRyaXgiID0gIm1lZGl1bXNlYWdyZWVuIiwKICAgICAgICAgICAgICAgICAgICJub24gbWF0cml4IiA9ICJmaXJlYnJpY2szIikKYGBgCgpXZSBkZWZpbmUgbWFya2VycyB0byBkaXNwbGF5IG9uIGEgZG90cGxvdCB0byBhc3Nlc3MgY2VsbCB0eXBlIGFubm90YXRpb24gOgoKYGBge3IgZG90cGxvdF9tYXJrZXJzfQpkb3RwbG90X21hcmtlcnMgPSBjKCJQVFBSQyIsICAgICAgICAgICAgICAgICMgaW1tdW5lIGNlbGxzCiAgICAgICAgICAgICAgICAgICAgIkNEM0UiLCAiQ0Q0IiwgICAgICAgICAgIyBDRDQrIFQgY2VsbHMKICAgICAgICAgICAgICAgICAgICAiQ0QzRSIsICJDRDhBIiwgICAgICAgICAjIENEOCsgVCBjZWxscwogICAgICAgICAgICAgICAgICAgICJDRDIwNyIsICJBSUYxIiwgICAgICAgICMgTGFuZ2VyaGFucyBjZWxscwogICAgICAgICAgICAgICAgICAgICJUUkVNMiIsICJNU1IxIiwgICAgICAgICMgbWFjcm9waGFnZXMKICAgICAgICAgICAgICAgICAgICAiQ0Q3OUEiLCAiQ0Q3OUIiLCAgICAgICAjIEIgY2VsbHMKICAgICAgICAgICAgICAgICAgICAiTVNYMiIsICAgICAgICAgICAgICAgICAjIG1hdHJpeCBjZWxscwogICAgICAgICAgICAgICAgICAgICJLUlQzMiIsICJLUlQzNSIsICAgICAgICMgY3V0aWNsZQogICAgICAgICAgICAgICAgICAgICJLUlQzMSIsICJQUlI5IiwgICAgICAgICMgY29ydGV4CiAgICAgICAgICAgICAgICAgICAgIkJBTUJJIiwgIkFMREgxQTMiLCAgICAgIyBtZWR1bGxhCiAgICAgICAgICAgICAgICAgICAgIktSVDcxIiwgIktSVDczIiwgICAgICAgIyBJUlMKICAgICAgICAgICAgICAgICAgICAiVE9QMkEiLCAiTUNNNSIsICJUSzEiLCAjIGN5Y2xpbmcgY2VsbHMKICAgICAgICAgICAgICAgICAgICAiS1JUMTQiLCAiQ1hDTDE0IiwgICAgICAjIG5vbi1tYXRyaXggY2VsbHMKICAgICAgICAgICAgICAgICAgICAiRElPMiIsICJUQ0VBTDIiLCAgICAgICAjIEhGLVNDcwogICAgICAgICAgICAgICAgICAgICJLUlQxNSIsICJDT0wxN0ExIiwgICAgICMgSUZFIGJhc2FsCiAgICAgICAgICAgICAgICAgICAgIlNQSU5LNSIsICJLUlQxIiwgICAgICAgIyBPUlMKICAgICAgICAgICAgICAgICAgICAiS1JUMTYiLCAiS1JUNkMiLCAgICAgICAjIElGRSBncmFudWxhciBvciBzcGlub3VzCiAgICAgICAgICAgICAgICAgICAgIkNMTVAiLCAiUFBBUkciKSAgICAgICAgIyBzZWJvY3l0ZXMKYGBgCgpXZSBjb3B5LXBhc3RlIGEgc3Vic2V0IG9mIHRoZSBtYXJrZXJzIGFzc29jaWF0ZWQgd2l0aCBjZWxsIHR5cGUgYW5ub3RhdGlvbiA6CgpgYGB7ciBjZWxsX21hcmtlcnN9CmNlbGxfbWFya2VycyA9IGxpc3QoCiAgIyAgSW1tdW5lIGNlbGxzCiAgIkNENCBUIGNlbGxzIiA9IGMoIkNEM0QiLCAiQ0QzRyIsICJDRDNFIiwgIkNENTIiLCAiSUwzMiIsCiAgICAgICAgICAgICAgICAgICAgIlRSQkMxIiwgIkNENCIsICJDT1RMMSIsICJDRDQwTEciLCAiR1BSMTgzIiwgIlRORlJTRjFCIiwgIkZZQjEiLCAiTEFUIiksCiAgIkNEOCBUIGNlbGxzIiA9IGMoIkNEM0QiLCAiQ0QzRyIsICJDRDNFIiwgIkNENTIiLCAiSUwzMiIsCiAgICAgICAgICAgICAgICAgICAgIkNUU1ciLCAiTkNSMyIsICJDRDhBIiwgIk5LRzciLCAiS0xSQzEiLCAiVFJHQzIiLCAiQ0NMNSIsICJaTkY2ODMiKSwKICAiTGFuZ2VyaGFucyBjZWxscyIgPSBjKCJDRDIwNyIsICJMU1QxIiwgIkFJRjEiLCAiQ1BWTCIsICJDMTVvcmY0OCIsICJDRDFBIiwgIkNENTIiLCAiQ0QxRSIsICJDRDFDIiksCiAgIm1hY3JvcGhhZ2VzIiA9IGMoIkxTVDEiLCAiQUlGMSIsICJDMyIsICJPTEZNTDMiLCAiVFJFTTIiLCAiQTJNIiwKICAgICAgICAgICAgICAgICAgICAiTVNSMSIsICJGQ0dSM0EiLCAiVlNJRzQiLCAiQVAwMDM0ODEuMSIpLAogICJCIGNlbGxzIiA9IGMoIkNENzlBIiwgIklHSEcyIiwgIklHSEczIiwgIklHTEMzIiwgIklHSEcxIiwgIklHSE0iLCAiTVM0QTEiLCAiSUdMQzIiLAogICAgICAgICAgICAgICAgIklHSEdQIiwgIkJBTksxIiwgIklHSEc0IiwgIklHS0MiLCAiVE5GUlNGMTNDIiwgIkNENzlCIiwgIktMRjIiLCAiR1pNQiIpLAogICMgTWF0cml4CiAgImN1dGljbGUiID0gYygiS1JUMzIiLCAiQ1lTVE0xIiwgIk1UNCIsICJLUlQzNSIsICJWU05MMSIsICJOT1RDSDEiKSwKICAiY29ydGV4IiA9IGMoIkMxMG9yZjk5IiwgIkNSWUFCIiwgIkhFUEhMMSIsICJBTE9YMTVCIiwgIkVGSEQxIiwgIkRTRzQiLCAiRE5BU0UxTDIiLCAiVEdNMyIsICJLUlQzMSIpLAogICJtZWR1bGxhIiA9IGMoIkJBTUJJIiwgIlNMQzdBOCIsICJFRE5SQSIsICJJR0ZCUDIiLCAiS1JUMjUiLCAiS0lUTEciKSwKICAiSVJTIiA9IGMoIktSVDcxIiwgIktSVDI3IiwgIktSVDI4IiwgIkNUU0MiLCAiR0FUQTMiLCAiS1JUNzMiLCAiU0NFTCIpLAogICJwcm9saWZlcmF0aXZlIiA9IGMoIlRPUDJBIiwgIk1LSTY3IiwgIkJJUkM1IiwgIlRLMSIsICJNQ001IiksCiAgIyBOb24tbWF0cml4CiAgIkhGLVNDcyIgPSBjKCJLUlQxNSIsICJEU1QiLCAiRElPMiIsICJUQ0VBTDIiLCAiU0ZSUDEiLCAiVE5DIiwgIldJRjEiLCAiRlJaQiIsICJMSFgyIiwgIkNPTDE3QTEiKSwKICAiSUZFIGJhc2FsIiA9IGMoIkdQWDIiLCAiTU9YRDEiLCAiQzE5b3JmNDgiLCAiQUxESDNBMSIsICJDREgxMyIsICJMQU1CMyIsICJDQVZJTjEiKSwKICAiSUZFIGdyYW51bGFyIHNwaW5vdXMiID0gYygiREVGQjEiLCAiTFk2RCIsICJFSEYiLCAiUERaSzFJUDEiLCAiUzEwMEE3IiwgIkZHRkJQMSIsICJTUElOSzUiLCAiS1JUMSIsICJLUlREQVAiKSwKICAiT1JTIiA9IGMoIktSVDE2IiwgIktSVDZCIiwgIktSVDZDIiwgIlRNNFNGMSIsICJBUE9DMSIsICJDQkxOMiIsICJIRVM0IiwgIkxZUEQzIiksCiAgIyBvdGhlcgogICJtZWxhbm9jeXRlcyIgPSBjKCJEQ1QiLCAiS0lUIiwgIk1MQU5BIiwgIkdQTTZCIiwgIkVETlJCIiwgIlBNRUwiLCAiSUdGQlA3IiwgIk1JVEYiLCAiWkVCMiIsICJBUE9EIiksCiAgInNlYm9jeXRlcyIgPSBjKCJDTE1QIiwgIlBQQVJHIiwgIkFDU0JHMSIsICJNR1NUMSIsICJTQUExIiwgIkFQTUFQIikpCgpsZW5ndGhzKGNlbGxfbWFya2VycykKYGBgCgpDdXN0b20gZnVuY3Rpb25zIHRvIGRpc3BsYXkgZ2VuZSBleHByZXNzaW9uIG9uIHRoZSBoZWF0bWFwIDoKCmBgYHtyIGNvbG9yX2Z1bn0KY29sb3JfZnVuID0gZnVuY3Rpb24ob25lX2dlbmUpIHsKICBnZW5lX3JhbmdlID0gcmFuZ2UoaHRfYW5ub3RbLCBvbmVfZ2VuZV0pCiAgZ2VuZV9wYWxldHRlID0gY2lyY2xpemU6OmNvbG9yUmFtcDIoY29sb3JzID0gYygiI0ZGRkZGRiIsIGFxdWFyaXVzOjpjb2xvcl9nZW5lWy0xXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKGZyb20gPSBnZW5lX3JhbmdlWzFdLCB0byA9IGdlbmVfcmFuZ2VbMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSBsZW5ndGgoYXF1YXJpdXM6OmNvbG9yX2dlbmUpKSkKICByZXR1cm4oZ2VuZV9wYWxldHRlKQp9CmBgYAoKIyBMb2FkIGRhdGFzZXRzCgpJbiBhIGxpc3QsIHdlIGxvYWQ6CgoqIFNldXJhdCBvYmplY3QKKiBuYW1lIG9mIHRoZSAyRCBwcm9qZWN0aW9uIHRvIHZpc3VhbGl6ZSBjZWxscyBvbgoqIHZhcmlvdXMgdGhpcmQgZWxlbWVudCBzdWNoIGFzOiBzYW1wbGUgaW5mb3JtYXRpb24sIHRyYWplY3RvcnksIHJlc3VsdHMgZnJvbSBnZW5lIGFuYWx5c2lzLi4uCgpgYGB7ciBsaXN0X2RhdGF9Cmxpc3RfZGF0YSA9IGxpc3QoKQoKIyMgT3VyIGRhdGFzZXQKIyBBdGxhcyBvZiBhbGwgY2VsbHMKbGlzdF9kYXRhJG91cl9hdGxhcyRzb2JqID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvM19jb21iaW5lZC9oc19oZF9zb2JqLnJkcyIpKQpsaXN0X2RhdGEkb3VyX2F0bGFzJG5hbWUyRCA9ICJoYXJtb255XzM4X3RzbmUiCmxpc3RfZGF0YSRvdXJfYXRsYXMkc2FtcGxlX2luZm8gPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi8xX21ldGFkYXRhL2hzX2hkX3NhbXBsZV9pbmZvLnJkcyIpKQpsaXN0X2RhdGEkb3VyX2F0bGFzJHNvYmoKCiMgT1JTICsgSUZFYiBkYXRhc2V0Cmxpc3RfZGF0YSRvcnNfaWZlYiRzb2JqID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNF96b29tLzNfem9vbV9vcnNfaWZlYi9vcnNfaWZlYl9zb2JqLnJkcyIpKQpsaXN0X2RhdGEkb3JzX2lmZWIkbmFtZTJEID0gImhhcm1vbnlfMjBfdHNuZSIKbGlzdF9kYXRhJG9yc19pZmViJGxpc3RfcmVzdWx0cyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS8zX3pvb21fb3JzX2lmZWIvb3JzX2lmZWJfbGlzdF9yZXN1bHRzLnJkcyIpKQpsaXN0X2RhdGEkb3JzX2lmZWIkc29iagoKIyBIRi1TQ3MgZGF0YXNldApsaXN0X2RhdGEkaGZzYyRzb2JqID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNF96b29tLzJfem9vbV9oZnNjL2hmc2Nfc29iai5yZHMiKSkKbGlzdF9kYXRhJGhmc2MkbmFtZTJEID0gImhhcm1vbnlfMjRfdHNuZSIKbGlzdF9kYXRhJGhmc2MkbGlzdF9yZXN1bHRzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNF96b29tLzJfem9vbV9oZnNjL2hmc2NfbGlzdF9yZXN1bHRzLnJkcyIpKQpsaXN0X2RhdGEkaGZzYyRzb2JqCgojIE5vbiBtYXRyaXggY2VsbHMgZGF0YXNldApsaXN0X2RhdGEkbm9uX21hdHJpeCRzb2JqID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNF96b29tLzVfem9vbV9ub25fbWF0cml4L25vbl9tYXRyaXhfc29ial90cmFqX3RpbmdhLnJkcyIpKQpsaXN0X2RhdGEkbm9uX21hdHJpeCRuYW1lMkQgPSAiaGFybW9ueV9kbSIKbGlzdF9kYXRhJG5vbl9tYXRyaXgkbXlfdHJhaiA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS81X3pvb21fbm9uX21hdHJpeC9ub25fbWF0cml4X215X3RyYWpfdGluZ2EucmRzIikpCmxpc3RfZGF0YSRub25fbWF0cml4JHNvYmoKCiMgTWF0cml4IGNlbGxzIGRhdGFzZXQgKG5vdCBjb25zaWRlcmVkIGluIHRoZSBtYW51c2NyaXB0KQojIGxpc3RfZGF0YSRtYXRyaXgkc29iaiA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS82X3pvb21fbWF0cml4L21hdHJpeF9zb2JqLnJkcyIpKQojIGxpc3RfZGF0YSRtYXRyaXgkbmFtZTJEID0gImhhcm1vbnlfMTlfdHNuZSIKIyBsaXN0X2RhdGEkbWF0cml4JGxpc3RfcmVzdWx0cyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS82X3pvb21fbWF0cml4L21hdHJpeF9saXN0X3Jlc3VsdHMucmRzIikpCiMgbGlzdF9kYXRhJG1hdHJpeCRzb2JqCgojIEltbXVuZSBjZWxscwpsaXN0X2RhdGEkaW1tdW5lJHNvYmogPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi80X3pvb20vMV96b29tX2ltbXVuZS9pbW11bmVfY2VsbHNfc29iai5yZHMiKSkKbGlzdF9kYXRhJGltbXVuZSRuYW1lMkQgPSAiaGFybW9ueV8yMF90c25lIgpsaXN0X2RhdGEkaW1tdW5lJGxpc3RfcmVzdWx0cyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS8xX3pvb21faW1tdW5lL2ltbXVuZV9jZWxsc19saXN0X3Jlc3VsdHMucmRzIikpCmxpc3RfZGF0YSRpbW11bmUkc29iagoKIyMgV3UgZGF0YXNldCAoT0VQMDAyMzIxKQojIEF0bGFzIG9mIGFsbCBjZWxscwpsaXN0X2RhdGEkd3VfYXRsYXMkc29iaiA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzVfd3UvM19jb21iaW5lZC93dV9zb2JqLnJkcyIpKQpsaXN0X2RhdGEkd3VfYXRsYXMkbmFtZTJEID0gImhhcm1vbnlfMzlfdHNuZSIKbGlzdF9kYXRhJHd1X2F0bGFzJHNhbXBsZV9pbmZvID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNV93dS8xX21ldGFkYXRhL3d1X3NhbXBsZV9pbmZvLnJkcyIpKQoKIyBPUlMgKyBJRkViIGRhdGFzZXQKbGlzdF9kYXRhJHd1X29yc19pZmViJHNvYmogPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi81X3d1LzRfb3JzX2lmZWIvb3JzX2lmZWJfc29iai5yZHMiKSkKbGlzdF9kYXRhJHd1X29yc19pZmViJG5hbWUyRCA9ICJoYXJtb255XzIwX3RzbmUiCmxpc3RfZGF0YSR3dV9vcnNfaWZlYiRsaXN0X3Jlc3VsdHMgPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi81X3d1LzRfb3JzX2lmZWIvb3JzX2lmZWJfbGlzdF9yZXN1bHRzLnJkcyIpKQpsaXN0X2RhdGEkd3Vfb3JzX2lmZWIkc29iagoKIyMgVGFrYWhhc2hpIGRhdGFzZXQgKEdTRTEyOTYxMSkKIyBBdGxhcyBvZiBhbGwgY2VsbHMKbGlzdF9kYXRhJHRha2FoYXNoaV9hdGxhcyRzb2JqID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNl90YWthaGFzaGkvM19jb21iaW5lZC90YWthaGFzaGlfc29iai5yZHMiKSkKbGlzdF9kYXRhJHRha2FoYXNoaV9hdGxhcyRuYW1lMkQgPSAiaGFybW9ueV80MV90c25lIgpsaXN0X2RhdGEkdGFrYWhhc2hpX2F0bGFzJHNhbXBsZV9pbmZvID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNl90YWthaGFzaGkvMV9tZXRhZGF0YS90YWthaGFzaGlfc2FtcGxlX2luZm8ucmRzIikpCmxpc3RfZGF0YSR0YWthaGFzaGlfYXRsYXMkc29iagoKIyBPUlMgKyBJRkViIGRhdGFzZXQKbGlzdF9kYXRhJHRha2FoYXNoaV9vcnNfaWZlYiRzb2JqID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNl90YWthaGFzaGkvNF9vcnNfaWZlYi9vcnNfaWZlYl9zb2JqLnJkcyIpKQpsaXN0X2RhdGEkdGFrYWhhc2hpX29yc19pZmViJG5hbWUyRCA9ICJoYXJtb255XzIzX3RzbmUiCmxpc3RfZGF0YSR0YWthaGFzaGlfb3JzX2lmZWIkbGlzdF9yZXN1bHRzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNl90YWthaGFzaGkvNF9vcnNfaWZlYi9vcnNfaWZlYl9saXN0X3Jlc3VsdHMucmRzIikpCmxpc3RfZGF0YSR0YWthaGFzaGlfb3JzX2lmZWIkc29iagoKIyMgVGhyZWUgZGF0YXNldHMgY29tYmluZWQgKE91ciArIE9FUDAwMjMyMSArIEdTRTEyOTYxMSkKbGlzdF9kYXRhJGNvbWJpbmVkJHNvYmogPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi8zX2NvbWJpbmVkL2RhdGEzX3NvYmoucmRzIikpCmxpc3RfZGF0YSRjb21iaW5lZCRuYW1lMkQgPSAiaGFybW9ueV8zN190c25lIgpsaXN0X2RhdGEkY29tYmluZWQkc29iagoKIyMgUHJpbnQKc3RyKGxpc3RfZGF0YSwgbWF4LmxldmVsID0gMikKYGBgCgoKIyBGaWd1cmVzCgojIyBTYW1wbGUgaW5mb3JtYXRpb24KCk91ciBkYXRhc2V0OgoKYGBge3Igc2FtcGxlX2luZm9fb3VyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDN9CnNvYmogPSBsaXN0X2RhdGEkb3VyX2F0bGFzJHNvYmoKc2FtcGxlX2luZm8gPSBsaXN0X2RhdGEkb3VyX2F0bGFzJHNhbXBsZV9pbmZvCgojIE5iIGNlbGxzIGJ5IGRhdGFzZXQKdG9fcGxvdCA9IHRhYmxlKHNvYmokc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoLiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUKICBgY29sbmFtZXM8LWAoYygic2FtcGxlX2lkZW50aWZpZXIiLCAibmJfY2VsbHMiKSkgJT4lCiAgZHBseXI6OmxlZnRfam9pbih4ID0gLiwgeSA9IHNhbXBsZV9pbmZvLCBieSA9ICJzYW1wbGVfaWRlbnRpZmllciIpIAoKIyBwYXRjaHdvcmsKcGxvdF9saXN0ID0gYXF1YXJpdXM6OmZpZ19wbG90X2diKHRvX3Bsb3QsIHRpdGxlID0gIkF2YWlsYWJsZSBkYXRhc2V0cyIpCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QpICsKICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KGRlc2lnbiA9ICJBXG5CIiwgaGVpZ2h0cyA9IGMoMC4xLDUpKSAmCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE1KSkKYGBgCgpXdSBkYXRhc2V0OgoKYGBge3Igc2FtcGxlX2luZm9fd3UsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gM30Kc29iaiA9IGxpc3RfZGF0YSR3dV9hdGxhcyRzb2JqCnNhbXBsZV9pbmZvID0gbGlzdF9kYXRhJHd1X2F0bGFzJHNhbXBsZV9pbmZvCgojIE5iIGNlbGxzIGJ5IGRhdGFzZXQKdG9fcGxvdCA9IHRhYmxlKHNvYmokc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoLiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUKICBgY29sbmFtZXM8LWAoYygic2FtcGxlX2lkZW50aWZpZXIiLCAibmJfY2VsbHMiKSkgJT4lCiAgZHBseXI6OmxlZnRfam9pbih4ID0gLiwgeSA9IHNhbXBsZV9pbmZvLCBieSA9ICJzYW1wbGVfaWRlbnRpZmllciIpIAoKIyBwYXRjaHdvcmsKcGxvdF9saXN0ID0gYXF1YXJpdXM6OmZpZ19wbG90X2diKHRvX3Bsb3QsIHRpdGxlID0gIkF2YWlsYWJsZSBkYXRhc2V0cyIpCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QpICsKICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KGRlc2lnbiA9ICJBXG5CIiwgaGVpZ2h0cyA9IGMoMC4xLDUpKSAmCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE1KSkKYGBgCgpUYWthaGFzaGkgZGF0YXNldDoKCmBgYHtyIHNhbXBsZV9pbmZvX3Rha2FoYXNoaSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAzfQpzb2JqID0gbGlzdF9kYXRhJHRha2FoYXNoaV9hdGxhcyRzb2JqCnNhbXBsZV9pbmZvID0gbGlzdF9kYXRhJHRha2FoYXNoaV9hdGxhcyRzYW1wbGVfaW5mbwoKIyBOYiBjZWxscyBieSBkYXRhc2V0CnRvX3Bsb3QgPSB0YWJsZShzb2JqJHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKC4sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoInNhbXBsZV9pZGVudGlmaWVyIiwgIm5iX2NlbGxzIikpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oeCA9IC4sIHkgPSBzYW1wbGVfaW5mbywgYnkgPSAic2FtcGxlX2lkZW50aWZpZXIiKSAKCiMgcGF0Y2h3b3JrCnBsb3RfbGlzdCA9IGFxdWFyaXVzOjpmaWdfcGxvdF9nYih0b19wbG90LCB0aXRsZSA9ICJBdmFpbGFibGUgZGF0YXNldHMiKQpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0KSArCiAgcGF0Y2h3b3JrOjpwbG90X2xheW91dChkZXNpZ24gPSAiQVxuQiIsIGhlaWdodHMgPSBjKDAuMSw1KSkgJgogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNSkpCmBgYAoKIyMgQW5ub3RhdGlvbiBzbW9vdGhpbmcKCkZvciBlYWNoIGRhdGFzZXQsIHdlIHNtb290aCB0aGUgc2luZ2xlLWNlbGwgbGV2ZWwgY2VsbCB0eXBlIGFubm90YXRpb24gYXQgYSBjbHVzdGVyIGxldmVsLgoKKiBPdXIgZGF0YXNldDoKCmBgYHtyIHNtb290aGluZ19hdGxhc19vdXJ9CiMgU2VsZWN0IGRhdGFzZXQKc29iaiA9IGxpc3RfZGF0YSRvdXJfYXRsYXMkc29iagoKIyBBbm5vdGF0aW9uIHNtb290aGluZwpzb2JqJGNlbGxfdHlwZSA9IHNvYmokY2VsbF90eXBlICU+JQogIGFzLmNoYXJhY3RlcigpICU+JQogIGZhY3RvciguLCBsZXZlbHMgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkKCmNsdXN0ZXJfdHlwZSA9IHRhYmxlKHNvYmokY2VsbF90eXBlLCBzb2JqJHNldXJhdF9jbHVzdGVycykgJT4lCiAgcHJvcC50YWJsZSguLCBtYXJnaW4gPSAyKSAlPiUKICBhcHBseSguLCAyLCB3aGljaC5tYXgpCmNsdXN0ZXJfdHlwZSA9IHNldE5hbWVzKG5tID0gbmFtZXMoY2x1c3Rlcl90eXBlKSwKICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzKHNvYmokY2VsbF90eXBlKVtjbHVzdGVyX3R5cGVdKQoKc29iaiRjbHVzdGVyX3R5cGUgPSBjbHVzdGVyX3R5cGVbc29iaiRzZXVyYXRfY2x1c3RlcnNdCnNvYmokY2x1c3Rlcl90eXBlID0gZmFjdG9yKHNvYmokY2x1c3Rlcl90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBsZXZlbHMoc29iaiRjZWxsX3R5cGUpKQoKIyBDb25zaWRlciBjaGFuZ2UKbGlzdF9kYXRhJG91cl9hdGxhcyRzb2JqID0gc29iagpgYGAKCmBgYHtyIHNtb290aGluZ19ub25fbWF0cml4X291cn0KIyBTZWxlY3QgZGF0YXNldApzb2JqID0gbGlzdF9kYXRhJG5vbl9tYXRyaXgkc29iagoKIyBBbm5vdGF0aW9uIHNtb290aGluZwpzb2JqJGNlbGxfdHlwZSA9IHNvYmokY2VsbF90eXBlICU+JQogIGFzLmNoYXJhY3RlcigpICU+JQogIGZhY3RvciguLCBsZXZlbHMgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkKCmNsdXN0ZXJfdHlwZSA9IHRhYmxlKHNvYmokY2VsbF90eXBlLCBzb2JqJHNldXJhdF9jbHVzdGVycykgJT4lCiAgcHJvcC50YWJsZSguLCBtYXJnaW4gPSAyKSAlPiUKICBhcHBseSguLCAyLCB3aGljaC5tYXgpCmNsdXN0ZXJfdHlwZSA9IHNldE5hbWVzKG5tID0gbmFtZXMoY2x1c3Rlcl90eXBlKSwKICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzKHNvYmokY2VsbF90eXBlKVtjbHVzdGVyX3R5cGVdKQoKc29iaiRjbHVzdGVyX3R5cGUgPSBjbHVzdGVyX3R5cGVbc29iaiRzZXVyYXRfY2x1c3RlcnNdCnNvYmokY2x1c3Rlcl90eXBlID0gZmFjdG9yKHNvYmokY2x1c3Rlcl90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBsZXZlbHMoc29iaiRjZWxsX3R5cGUpKQoKIyBDb25zaWRlciBjaGFuZ2UKbGlzdF9kYXRhJG5vbl9tYXRyaXgkc29iaiA9IHNvYmoKYGBgCgpgYGB7ciBzbW9vdGhpbmdfaW1tdW5lX291cn0KIyBTZWxlY3QgZGF0YXNldApzb2JqID0gbGlzdF9kYXRhJGltbXVuZSRzb2JqCgojIEFubm90YXRpb24gc21vb3RoaW5nCnNvYmokY2VsbF90eXBlID0gc29iaiRjZWxsX3R5cGUgJT4lCiAgYXMuY2hhcmFjdGVyKCkgJT4lCiAgZmFjdG9yKC4sIGxldmVscyA9IG5hbWVzKGNvbG9yX21hcmtlcnMpKQoKY2x1c3Rlcl90eXBlID0gdGFibGUoc29iaiRjZWxsX3R5cGUsIHNvYmokc2V1cmF0X2NsdXN0ZXJzKSAlPiUKICBwcm9wLnRhYmxlKC4sIG1hcmdpbiA9IDIpICU+JQogIGFwcGx5KC4sIDIsIHdoaWNoLm1heCkKY2x1c3Rlcl90eXBlID0gc2V0TmFtZXMobm0gPSBuYW1lcyhjbHVzdGVyX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMoc29iaiRjZWxsX3R5cGUpW2NsdXN0ZXJfdHlwZV0pCgpzb2JqJGNsdXN0ZXJfdHlwZSA9IGNsdXN0ZXJfdHlwZVtzb2JqJHNldXJhdF9jbHVzdGVyc10Kc29iaiRjbHVzdGVyX3R5cGUgPSBmYWN0b3Ioc29iaiRjbHVzdGVyX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGxldmVscyhzb2JqJGNlbGxfdHlwZSkpCgojIENvbnNpZGVyIGNoYW5nZQpsaXN0X2RhdGEkaW1tdW5lJHNvYmogPSBzb2JqCmBgYAoKKiBXdSBkYXRhc2V0OgoKYGBge3Igc21vb3RoaW5nX3d1fQojIFNlbGVjdCBkYXRhc2V0CnNvYmogPSBsaXN0X2RhdGEkd3VfYXRsYXMkc29iagoKIyBBbm5vdGF0aW9uIHNtb290aGluZwpzb2JqJGNlbGxfdHlwZSA9IHNvYmokY2VsbF90eXBlICU+JQogIGFzLmNoYXJhY3RlcigpICU+JQogIGZhY3RvciguLCBsZXZlbHMgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkKCmNsdXN0ZXJfdHlwZSA9IHRhYmxlKHNvYmokY2VsbF90eXBlLCBzb2JqJHNldXJhdF9jbHVzdGVycykgJT4lCiAgcHJvcC50YWJsZSguLCBtYXJnaW4gPSAyKSAlPiUKICBhcHBseSguLCAyLCB3aGljaC5tYXgpCmNsdXN0ZXJfdHlwZSA9IHNldE5hbWVzKG5tID0gbmFtZXMoY2x1c3Rlcl90eXBlKSwKICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzKHNvYmokY2VsbF90eXBlKVtjbHVzdGVyX3R5cGVdKQoKc29iaiRjbHVzdGVyX3R5cGUgPSBjbHVzdGVyX3R5cGVbc29iaiRzZXVyYXRfY2x1c3RlcnNdCnNvYmokY2x1c3Rlcl90eXBlID0gZmFjdG9yKHNvYmokY2x1c3Rlcl90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBsZXZlbHMoc29iaiRjZWxsX3R5cGUpKQoKIyBDb25zaWRlciBjaGFuZ2UKbGlzdF9kYXRhJHd1X2F0bGFzJHNvYmogPSBzb2JqCmBgYAoKKiBUYWthaGFzaGkgZGF0YXNldDoKCmBgYHtyIHNtb290aGluZ190YWthaGFzaGl9CiMgU2VsZWN0IGRhdGFzZXQKc29iaiA9IGxpc3RfZGF0YSR0YWthaGFzaGlfYXRsYXMkc29iagoKIyBBbm5vdGF0aW9uIHNtb290aGluZwpzb2JqJGNlbGxfdHlwZSA9IHNvYmokY2VsbF90eXBlICU+JQogIGFzLmNoYXJhY3RlcigpICU+JQogIGZhY3RvciguLCBsZXZlbHMgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSkKCmNsdXN0ZXJfdHlwZSA9IHRhYmxlKHNvYmokY2VsbF90eXBlLCBzb2JqJHNldXJhdF9jbHVzdGVycykgJT4lCiAgcHJvcC50YWJsZSguLCBtYXJnaW4gPSAyKSAlPiUKICBhcHBseSguLCAyLCB3aGljaC5tYXgpCmNsdXN0ZXJfdHlwZSA9IHNldE5hbWVzKG5tID0gbmFtZXMoY2x1c3Rlcl90eXBlKSwKICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzKHNvYmokY2VsbF90eXBlKVtjbHVzdGVyX3R5cGVdKQoKc29iaiRjbHVzdGVyX3R5cGUgPSBjbHVzdGVyX3R5cGVbc29iaiRzZXVyYXRfY2x1c3RlcnNdCnNvYmokY2x1c3Rlcl90eXBlID0gZmFjdG9yKHNvYmokY2x1c3Rlcl90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBsZXZlbHMoc29iaiRjZWxsX3R5cGUpKQoKIyBDb25zaWRlciBjaGFuZ2UKbGlzdF9kYXRhJHRha2FoYXNoaV9hdGxhcyRzb2JqID0gc29iagpgYGAKCiogQ29tYmluZWQgZGF0YXNldDoKCmBgYHtyIHNtb290aGluZ19jb21iaW5lZH0KIyBTZWxlY3QgZGF0YXNldApzb2JqID0gbGlzdF9kYXRhJGNvbWJpbmVkJHNvYmoKCiMgQW5ub3RhdGlvbiBzbW9vdGhpbmcKc29iaiRjZWxsX3R5cGUgPSBzb2JqJGNlbGxfdHlwZSAlPiUKICBhcy5jaGFyYWN0ZXIoKSAlPiUKICBmYWN0b3IoLiwgbGV2ZWxzID0gbmFtZXMoY29sb3JfbWFya2VycykpCgpjbHVzdGVyX3R5cGUgPSB0YWJsZShzb2JqJGNlbGxfdHlwZSwgc29iaiRzZXVyYXRfY2x1c3RlcnMpICU+JQogIHByb3AudGFibGUoLiwgbWFyZ2luID0gMikgJT4lCiAgYXBwbHkoLiwgMiwgd2hpY2gubWF4KQpjbHVzdGVyX3R5cGUgPSBzZXROYW1lcyhubSA9IG5hbWVzKGNsdXN0ZXJfdHlwZSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyhzb2JqJGNlbGxfdHlwZSlbY2x1c3Rlcl90eXBlXSkKCnNvYmokY2x1c3Rlcl90eXBlID0gY2x1c3Rlcl90eXBlW3NvYmokc2V1cmF0X2NsdXN0ZXJzXQpzb2JqJGNsdXN0ZXJfdHlwZSA9IGZhY3Rvcihzb2JqJGNsdXN0ZXJfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gbGV2ZWxzKHNvYmokY2VsbF90eXBlKSkKCiMgQ29uc2lkZXIgY2hhbmdlCmxpc3RfZGF0YSRjb21iaW5lZCRzb2JqID0gc29iagpgYGAKCkZvciB0aGUgY29tYmluZWQgZGF0YXNldCwgd2UgYWxzbyBkZWZpbmUgdGhlIGNlbGwgY2F0ZWdvcnk6CgpgYGB7ciBjZWxsX2NhdGVnb3J5X2NvbWJpbmVkfQojIFNlbGVjdCBkYXRhc2V0CnNvYmogPSBsaXN0X2RhdGEkY29tYmluZWQkc29iagoKIyBEZWZpbmUgY2VsbCBjYXRlZ29yeQpzb2JqJGNlbGxfY2F0ZWdvcnkgPSBjdXN0b21fb3JkZXJfY2VsbF90eXBlW3NvYmokY2VsbF90eXBlLCAiY2VsbF9jYXRlZ29yeSJdCnRhYmxlKHNvYmokY2VsbF90eXBlLCBzb2JqJGNlbGxfY2F0ZWdvcnkpCgojIENvbnNpZGVyIGNoYW5nZQpsaXN0X2RhdGEkY29tYmluZWQkc29iaiA9IHNvYmoKYGBgCgoKIyMgQXRsYXMKCiMjIyBPdXIgZGF0YXNldAoKV2Ugc2VsZWN0IHRoZSBkYXRhc2V0OgoKYGBge3Igb3VyX2RhdGFzZXR9CnNvYmogPSBsaXN0X2RhdGEkb3VyX2F0bGFzJHNvYmoKbmFtZTJEID0gbGlzdF9kYXRhJG91cl9hdGxhcyRuYW1lMkQKc2FtcGxlX2luZm8gPSBsaXN0X2RhdGEkb3VyX2F0bGFzJHNhbXBsZV9pbmZvCmBgYAoKKiBzYW1wbGUgaWRlbnRpZmllcgoKYGBge3Igc2FtcGxlX2lkX291ciwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9CiMgUmFuZG9tIG9yZGVyCnNldC5zZWVkKDEyMzQpCnJuZF9vcmRlciA9IHNhbXBsZShjb2xuYW1lcyhzb2JqKSwgcmVwbGFjZSA9IEZBTFNFLCBzaXplID0gbmNvbChzb2JqKSkKCiMgRXh0cmFjdCBjb29yZGluYXRlcwpjZWxsc19jb29yZCA9IHNvYmpAcmVkdWN0aW9uc1tbbmFtZTJEXV1AY2VsbC5lbWJlZGRpbmdzICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygiRGltMSIsICJEaW0yIikpCmNlbGxzX2Nvb3JkJHNhbXBsZV9pZGVudGlmaWVyID0gc29iaiRzYW1wbGVfaWRlbnRpZmllcgpjZWxsc19jb29yZCRsb2NhdGlvbiA9IHNvYmokbG9jYXRpb24KY2VsbHNfY29vcmQgPSBjZWxsc19jb29yZFsocm5kX29yZGVyKSwgXQoKIyBQbG90CmdncGxvdDI6OmdncGxvdChjZWxsc19jb29yZCwgYWVzKHggPSBEaW0xLCB5ID0gRGltMiwgY29sID0gc2FtcGxlX2lkZW50aWZpZXIpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzaXplID0gMC41KSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzYW1wbGVfaW5mbyRzYW1wbGVfaWRlbnRpZmllcikgKwogIGdncGxvdDI6OnRoZW1lX3ZvaWQoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKKiBsb2NhdGlvbgoKYGBge3IgbG9jYXRpb25fb3VyLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KZ2dwbG90Mjo6Z2dwbG90KGNlbGxzX2Nvb3JkLCBhZXMoeCA9IERpbTEsIHkgPSBEaW0yLCBjb2wgPSBsb2NhdGlvbikpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHNpemUgPSAwLjUpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gbG9jYXRpb25fY29sb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhsb2NhdGlvbl9jb2xvcnMpKSArCiAgZ2dwbG90Mjo6dGhlbWVfdm9pZCgpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoqIGNlbGwgdHlwZSBhbm5vdGF0aW9uCgpgYGB7ciBjZWxsX3R5cGVfb3VyLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCiogY2x1c3RlciB0eXBlIGFubm90YXRpb24KCmBgYHtyIGNsdXN0ZXJfdHlwZV9vdXIsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMC41LAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIiwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKKiBkb3RwbG90IGZvciBjZWxsIHR5cGUgYW5ub3RhdGlvbgoKYGBge3IgZG90cGxvdF9vdXIsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA4LjV9CnBsb3RfbGlzdCA9IGFxdWFyaXVzOjpwbG90X2RvdHBsb3Qoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJrZXJzID0gZG90cGxvdF9tYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsIGNvbHVtbl9uYW1lID0gImNlbGxfdHlwZSIsIG5iX2hsaW5lID0gMCkgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJsZWZ0IiwKICAgICAgICAgICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5ib3ggPSAidmVydGljYWwiLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5ib3gubWFyZ2luID0gbWFyZ2luKDAsNzAsMCwwKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQocmVwKDAsIDQpLCAiY20iKSkKCnAgPSBkYXRhLmZyYW1lKGNlbGxfdHlwZSA9IGZhY3RvcihsZXZlbHMoc29iaiRjZWxsX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gbGV2ZWxzKHNvYmokY2VsbF90eXBlKSkpICU+JQogIGdncGxvdDI6OmdncGxvdCguLCBhZXMoeCA9IGNlbGxfdHlwZSwgeSA9IDApKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzaXplID0gMCkgKwogIGdncGxvdDI6Omdlb21fc2VnbWVudChhZXMoeCA9IDAuNSwgeGVuZCA9IDUuNSwgeSA9IDAsIHllbmQgPSAwKSwgc2l6ZSA9IDYsIGNvbCA9IGNhdGVnb3J5X2NvbG9yWyJpbW11bmUgY2VsbHMiXSkgKwogIGdncGxvdDI6Omdlb21fc2VnbWVudChhZXMoeCA9IDUuNSwgeGVuZCA9IDEwLjUsIHkgPSAwLCB5ZW5kID0gMCksIHNpemUgPSA2LCBjb2wgPSBjYXRlZ29yeV9jb2xvclsibWF0cml4Il0pICsKICBnZ3Bsb3QyOjpnZW9tX3NlZ21lbnQoYWVzKHggPSAxMC41LCB4ZW5kID0gMTUuNSwgeSA9IDAsIHllbmQgPSAwKSwgc2l6ZSA9IDYsIGNvbCA9IGNhdGVnb3J5X2NvbG9yWyJub24gbWF0cml4Il0pICsKICBnZ3Bsb3QyOjpzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApLCBsaW1pdHMgPSBjKDAsMCkpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDEwLCBjb2xvciA9ICJibGFjayIpLAogICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDAsMC41LDAuNSwwKSwgImNtIikpCgpwbG90X2xpc3QgPSBwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDEsIGhlaWdodHMgPSBjKDI1LCAxKSkKcGxvdF9saXN0CmBgYAoKKiBiYXJwbG90IHdpdGggcHJvcG9ydGlvbiBvZiBjZWxscyBmb3IgY2VsbCB0eXBlcyBvZiBpbnRlcmVzdCwgYnkgc2FtcGxlIElECgpgYGB7ciBiYXJwbG90X2F0bGFzLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0KIyBDZWxscyBvZiBpbnRlcmVzdApjZWxsc19vaSA9IGMoIkNENCBUIGNlbGxzIiwgIm1hY3JvcGhhZ2VzIikKCiMgUHJvcG9ydGlvbiBvZiBjZWxscyBieSBzYW1wbGUgSUQKZGZfcHJvcG9ydGlvbiA9IHRhYmxlKHNvYmokY2x1c3Rlcl90eXBlLAogICAgICAgICAgICAgICAgICAgICAgc29iaiRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgcHJvcC50YWJsZSguLCBtYXJnaW4gPSAyKSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoImNsdXN0ZXJfdHlwZSIsICJzYW1wbGVfaWRlbnRpZmllciIsICJmcmVxIikpICU+JQogIGRwbHlyOjpmaWx0ZXIoY2x1c3Rlcl90eXBlICVpbiUgY2VsbHNfb2kpICU+JQogIGRwbHlyOjptdXRhdGUoY2x1c3Rlcl90eXBlID0gZmFjdG9yKGNsdXN0ZXJfdHlwZSwgbGV2ZWxzID0gY2VsbHNfb2kpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyZXEgPSAxMDAqZnJlcSkgIyBwZXJjZW50YWdlCgojIFBsb3QKZ2dwbG90Mjo6Z2dwbG90KGRmX3Byb3BvcnRpb24sIGFlcyh4ID0gc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGZyZXEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGNsdXN0ZXJfdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSBnZ3Bsb3QyOjpwb3NpdGlvbl9kb2RnZSgpLAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgd2lkdGggPSAwLjcpICsKICBnZ3Bsb3QyOjpsYWJzKHkgPSAiJSBvZiBjZWxscyBieSBzYW1wbGUiKSArCiAgZ2dwbG90Mjo6c2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwxNCkpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcl9tYXJrZXJzW2NlbGxzX29pXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjZWxsc19vaSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkNlbGwgVHlwZSIpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5NTAiKSwKICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoKSwKICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2xpbmUoKSwKICAgICAgICAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpCmBgYAoKIyMjIFd1IGRhdGFzZXQKCldlIHNlbGVjdCB0aGUgZGF0YXNldDoKCmBgYHtyIHd1X2RhdGFzZXR9CnNvYmogPSBsaXN0X2RhdGEkd3VfYXRsYXMkc29iagpuYW1lMkQgPSBsaXN0X2RhdGEkd3VfYXRsYXMkbmFtZTJECnNhbXBsZV9pbmZvID0gbGlzdF9kYXRhJHd1X2F0bGFzJHNhbXBsZV9pbmZvCmBgYAoKKiBzYW1wbGUgaWRlbnRpZmllcgoKYGBge3Igc2FtcGxlX2lkX3d1LCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KIyBSYW5kb20gb3JkZXIKc2V0LnNlZWQoMTIzNCkKcm5kX29yZGVyID0gc2FtcGxlKGNvbG5hbWVzKHNvYmopLCByZXBsYWNlID0gRkFMU0UsIHNpemUgPSBuY29sKHNvYmopKQoKIyBFeHRyYWN0IGNvb3JkaW5hdGVzCmNlbGxzX2Nvb3JkID0gc29iakByZWR1Y3Rpb25zW1tuYW1lMkRdXUBjZWxsLmVtYmVkZGluZ3MgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJEaW0xIiwgIkRpbTIiKSkKY2VsbHNfY29vcmQkc2FtcGxlX2lkZW50aWZpZXIgPSBzb2JqJHNhbXBsZV9pZGVudGlmaWVyCmNlbGxzX2Nvb3JkID0gY2VsbHNfY29vcmRbKHJuZF9vcmRlciksIF0KCiMgUGxvdApnZ3Bsb3QyOjpnZ3Bsb3QoY2VsbHNfY29vcmQsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbCA9IHNhbXBsZV9pZGVudGlmaWVyKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBzYW1wbGVfaW5mbyRjb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2FtcGxlX2luZm8kc2FtcGxlX2lkZW50aWZpZXIpICsKICBnZ3Bsb3QyOjp0aGVtZV92b2lkKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiogY2VsbCB0eXBlIGFubm90YXRpb24KCmBgYHtyIGNlbGxfdHlwZV93dSwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjZWxsX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgoqIGNsdXN0ZXIgdHlwZSBhbm5vdGF0aW9uCgpgYGB7ciBjbHVzdGVyX3R5cGVfd3UsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMC41LAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIiwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKKiBkb3RwbG90IGZvciBjZWxsIHR5cGUgYW5ub3RhdGlvbgoKYGBge3IgZG90cGxvdF93dSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDguNX0KcGxvdF9saXN0ID0gYXF1YXJpdXM6OnBsb3RfZG90cGxvdChzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmtlcnMgPSBkb3RwbG90X21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwgY29sdW1uX25hbWUgPSAiY2VsbF90eXBlIiwgbmJfaGxpbmUgPSAwKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImxlZnQiLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICAgICAgICAgbGVnZW5kLmJveCA9ICJ2ZXJ0aWNhbCIsCiAgICAgICAgICAgICAgICAgbGVnZW5kLmJveC5tYXJnaW4gPSBtYXJnaW4oMCw3MCwwLDApLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChyZXAoMCwgNCksICJjbSIpKQoKcCA9IGRhdGEuZnJhbWUoY2VsbF90eXBlID0gZmFjdG9yKGxldmVscyhzb2JqJGNlbGxfdHlwZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBsZXZlbHMoc29iaiRjZWxsX3R5cGUpKSkgJT4lCiAgZ2dwbG90Mjo6Z2dwbG90KC4sIGFlcyh4ID0gY2VsbF90eXBlLCB5ID0gMCkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHNpemUgPSAwKSArCiAgZ2dwbG90Mjo6Z2VvbV9zZWdtZW50KGFlcyh4ID0gMC41LCB4ZW5kID0gNS41LCB5ID0gMCwgeWVuZCA9IDApLCBzaXplID0gNiwgY29sID0gY2F0ZWdvcnlfY29sb3JbImltbXVuZSBjZWxscyJdKSArCiAgZ2dwbG90Mjo6Z2VvbV9zZWdtZW50KGFlcyh4ID0gNS41LCB4ZW5kID0gMTAuNSwgeSA9IDAsIHllbmQgPSAwKSwgc2l6ZSA9IDYsIGNvbCA9IGNhdGVnb3J5X2NvbG9yWyJtYXRyaXgiXSkgKwogIGdncGxvdDI6Omdlb21fc2VnbWVudChhZXMoeCA9IDEwLjUsIHhlbmQgPSAxNS41LCB5ID0gMCwgeWVuZCA9IDApLCBzaXplID0gNiwgY29sID0gY2F0ZWdvcnlfY29sb3JbIm5vbiBtYXRyaXgiXSkgKwogIGdncGxvdDI6OnNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsMCksIGxpbWl0cyA9IGMoMCwwKSkgKwogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gMTAsIGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMCwwLjUsMC41LDApLCAiY20iKSkKCnBsb3RfbGlzdCA9IHBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIHAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMSwgaGVpZ2h0cyA9IGMoMjUsIDEpKQpwbG90X2xpc3QKYGBgCgojIyMgVGFrYWhhc2hpIGRhdGFzZXQKCldlIHNlbGVjdCB0aGUgZGF0YXNldDoKCmBgYHtyIHRha2FoYXNoaV9kYXRhc2V0fQpzb2JqID0gbGlzdF9kYXRhJHRha2FoYXNoaV9hdGxhcyRzb2JqCm5hbWUyRCA9IGxpc3RfZGF0YSR0YWthaGFzaGlfYXRsYXMkbmFtZTJECnNhbXBsZV9pbmZvID0gbGlzdF9kYXRhJHRha2FoYXNoaV9hdGxhcyRzYW1wbGVfaW5mbwpgYGAKCiogc2FtcGxlIGlkZW50aWZpZXIKCmBgYHtyIHNhbXBsZV9pZF90YWthaGFzaGksIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQojIFJhbmRvbSBvcmRlcgpzZXQuc2VlZCgxMjM0KQpybmRfb3JkZXIgPSBzYW1wbGUoY29sbmFtZXMoc29iaiksIHJlcGxhY2UgPSBGQUxTRSwgc2l6ZSA9IG5jb2woc29iaikpCgojIEV4dHJhY3QgY29vcmRpbmF0ZXMKY2VsbHNfY29vcmQgPSBzb2JqQHJlZHVjdGlvbnNbW25hbWUyRF1dQGNlbGwuZW1iZWRkaW5ncyAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIkRpbTEiLCAiRGltMiIpKQpjZWxsc19jb29yZCRzYW1wbGVfaWRlbnRpZmllciA9IHNvYmokc2FtcGxlX2lkZW50aWZpZXIKY2VsbHNfY29vcmQgPSBjZWxsc19jb29yZFsocm5kX29yZGVyKSwgXQoKIyBQbG90CmdncGxvdDI6OmdncGxvdChjZWxsc19jb29yZCwgYWVzKHggPSBEaW0xLCB5ID0gRGltMiwgY29sID0gc2FtcGxlX2lkZW50aWZpZXIpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzaXplID0gMC41KSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzYW1wbGVfaW5mbyRzYW1wbGVfaWRlbnRpZmllcikgKwogIGdncGxvdDI6OnRoZW1lX3ZvaWQoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKKiBjZWxsIHR5cGUgYW5ub3RhdGlvbgoKYGBge3IgY2VsbF90eXBlX3Rha2FoYXNoaSwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjZWxsX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgoqIGNsdXN0ZXIgdHlwZSBhbm5vdGF0aW9uCgpgYGB7ciBjbHVzdGVyX3R5cGVfdGFrYWhhc2hpLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNsdXN0ZXJfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCiogZG90cGxvdCBmb3IgY2VsbCB0eXBlIGFubm90YXRpb24KCmBgYHtyIGRvdHBsb3RfdGFrYWhhc2hpLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOC41fQpwbG90X2xpc3QgPSBhcXVhcml1czo6cGxvdF9kb3RwbG90KHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFya2VycyA9IGRvdHBsb3RfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiLCBjb2x1bW5fbmFtZSA9ICJjZWxsX3R5cGUiLCBuYl9obGluZSA9IDApICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6OmNvbG9yX2dlbmUpICsKICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibGVmdCIsCiAgICAgICAgICAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICBsZWdlbmQuYm94ID0gInZlcnRpY2FsIiwKICAgICAgICAgICAgICAgICBsZWdlbmQuYm94Lm1hcmdpbiA9IG1hcmdpbigwLDcwLDAsMCksCiAgICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KHJlcCgwLCA0KSwgImNtIikpCgpwID0gZGF0YS5mcmFtZShjZWxsX3R5cGUgPSBmYWN0b3IobGV2ZWxzKHNvYmokY2VsbF90eXBlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGxldmVscyhzb2JqJGNlbGxfdHlwZSkpKSAlPiUKICBnZ3Bsb3QyOjpnZ3Bsb3QoLiwgYWVzKHggPSBjZWxsX3R5cGUsIHkgPSAwKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDApICsKICBnZ3Bsb3QyOjpnZW9tX3NlZ21lbnQoYWVzKHggPSAwLjUsIHhlbmQgPSA1LjUsIHkgPSAwLCB5ZW5kID0gMCksIHNpemUgPSA2LCBjb2wgPSBjYXRlZ29yeV9jb2xvclsiaW1tdW5lIGNlbGxzIl0pICsKICBnZ3Bsb3QyOjpnZW9tX3NlZ21lbnQoYWVzKHggPSA1LjUsIHhlbmQgPSAxMC41LCB5ID0gMCwgeWVuZCA9IDApLCBzaXplID0gNiwgY29sID0gY2F0ZWdvcnlfY29sb3JbIm1hdHJpeCJdKSArCiAgZ2dwbG90Mjo6Z2VvbV9zZWdtZW50KGFlcyh4ID0gMTAuNSwgeGVuZCA9IDE1LjUsIHkgPSAwLCB5ZW5kID0gMCksIHNpemUgPSA2LCBjb2wgPSBjYXRlZ29yeV9jb2xvclsibm9uIG1hdHJpeCJdKSArCiAgZ2dwbG90Mjo6c2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSwgbGltaXRzID0gYygwLDApKSArCiAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSAxMCwgY29sb3IgPSAiYmxhY2siKSwKICAgICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygwLDAuNSwwLjUsMCksICJjbSIpKQoKcGxvdF9saXN0ID0gcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSAxLCBoZWlnaHRzID0gYygyNSwgMSkpCnBsb3RfbGlzdApgYGAKCiMjIyBDb21iaW5lZCBkYXRhc2V0cwoKV2Ugc2VsZWN0IHRoZSBkYXRhc2V0OgoKYGBge3IgY29tYmluZWRfZGF0YXNldH0Kc29iaiA9IGxpc3RfZGF0YSRjb21iaW5lZCRzb2JqCm5hbWUyRCA9IGxpc3RfZGF0YSRjb21iaW5lZCRuYW1lMkQKc2FtcGxlX2luZm8gPSByYmluZChsaXN0X2RhdGEkb3VyX2F0bGFzJHNhbXBsZV9pbmZvLAogICAgICAgICAgICAgICAgICAgIGxpc3RfZGF0YSR3dV9hdGxhcyRzYW1wbGVfaW5mbywKICAgICAgICAgICAgICAgICAgICBsaXN0X2RhdGEkdGFrYWhhc2hpX2F0bGFzJHNhbXBsZV9pbmZvKQpgYGAKCiogc2FtcGxlIGlkZW50aWZpZXIKCmBgYHtyIHNhbXBsZV9pZF9jb21iaW5lZCwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9CiMgUmFuZG9tIG9yZGVyCnNldC5zZWVkKDEyMzQpCnJuZF9vcmRlciA9IHNhbXBsZShjb2xuYW1lcyhzb2JqKSwgcmVwbGFjZSA9IEZBTFNFLCBzaXplID0gbmNvbChzb2JqKSkKCiMgRXh0cmFjdCBjb29yZGluYXRlcwpjZWxsc19jb29yZCA9IHNvYmpAcmVkdWN0aW9uc1tbbmFtZTJEXV1AY2VsbC5lbWJlZGRpbmdzICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygiRGltMSIsICJEaW0yIikpCmNlbGxzX2Nvb3JkJHNhbXBsZV9pZGVudGlmaWVyID0gc29iaiRzYW1wbGVfaWRlbnRpZmllcgpjZWxsc19jb29yZCRsb2NhdGlvbiA9IHNvYmokbG9jYXRpb24KY2VsbHNfY29vcmQkbGFib3JhdG9yeSA9IHNvYmokbGFib3JhdG9yeQpjZWxsc19jb29yZCRnZW5kZXIgPSBkcGx5cjo6bGVmdF9qb2luKHNvYmpAbWV0YS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pbmZvLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gInByb2plY3RfbmFtZSIpWywgImdlbmRlciJdCmNlbGxzX2Nvb3JkID0gY2VsbHNfY29vcmRbKHJuZF9vcmRlciksIF0KCiMgUGxvdApnZ3Bsb3QyOjpnZ3Bsb3QoY2VsbHNfY29vcmQsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbCA9IHNhbXBsZV9pZGVudGlmaWVyKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBzYW1wbGVfaW5mbyRjb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2FtcGxlX2luZm8kc2FtcGxlX2lkZW50aWZpZXIpICsKICBnZ3Bsb3QyOjp0aGVtZV92b2lkKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiogbG9jYXRpb24KCmBgYHtyIGxvY2F0aW9uX2NvbWJpbmVkLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KZ2dwbG90Mjo6Z2dwbG90KGNlbGxzX2Nvb3JkLCBhZXMoeCA9IERpbTEsIHkgPSBEaW0yLCBjb2wgPSBsb2NhdGlvbikpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHNpemUgPSAwLjUpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gbG9jYXRpb25fY29sb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhsb2NhdGlvbl9jb2xvcnMpKSArCiAgZ2dwbG90Mjo6dGhlbWVfdm9pZCgpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoqIGxhYm9yYXRvcnkKCmBgYHtyIGxhYm9yYXRvcnlfY29tYmluZWQsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpnZ3Bsb3QyOjpnZ3Bsb3QoY2VsbHNfY29vcmQsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbCA9IGxhYm9yYXRvcnkpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzaXplID0gMC41KSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGxhYm9yYXRvcnlfY29sb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhsYWJvcmF0b3J5X2NvbG9ycykpICsKICBnZ3Bsb3QyOjp0aGVtZV92b2lkKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiogZ2VuZGVyCgpgYGB7ciBnZW5kZXJfY29tYmluZWQsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpnZ3Bsb3QyOjpnZ3Bsb3QoY2VsbHNfY29vcmQsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbCA9IGdlbmRlcikpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHNpemUgPSAwLjUpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZ2VuZGVyX2NvbG9ycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gbmFtZXMoZ2VuZGVyX2NvbG9ycykpICsKICBnZ3Bsb3QyOjp0aGVtZV92b2lkKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiogY2VsbCB0eXBlIGFubm90YXRpb24KCmBgYHtyIGNlbGxfdHlwZV9jb21iaW5lZCwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjZWxsX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgoqIGNsdXN0ZXIgdHlwZSBhbm5vdGF0aW9uCgpgYGB7ciBjbHVzdGVyX3R5cGVfY29tYmluZWQsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMC41LAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIiwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKKiBzcGxpdCBieSBsYWJvcmF0b3J5OgoKYGBge3IgY29tYmluZWRfc3BsaXQsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNX0KcGxvdF9saXN0ID0gYXF1YXJpdXM6OnBsb3Rfc3BsaXRfZGltcmVkKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBuYW1lMkQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGxpdF9ieSA9ICJsYWJvcmF0b3J5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5ID0gImNsdXN0ZXJfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9jb2xvciA9IGNvbG9yX21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZ19jb2xvciA9IGJnX2NvbG9yKQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbnJvdyA9IDEpICsKICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikgJgogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCiogZ2VuZSB0byBhc3Nlc3MgZ2xvYmFsIGFubm90YXRpb24KCmBgYHtyIGdlbmVfZ2xvYmFsX2NvbWJpbmVkLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KcGxvdF9saXN0ID0gbGFwcGx5KGMoIlBUUFJDIiwgIk1TWDIiLCAiS1JUMTQiKSwgRlVOID0gZnVuY3Rpb24ob25lX2dlbmUpIHsKICBwID0gU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCBmZWF0dXJlcyA9IG9uZV9nZW5lLCByZWR1Y3Rpb24gPSBuYW1lMkQpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSkgKwogICAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogICAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgICBTZXVyYXQ6Ok5vTGVnZW5kKCkKICAKICByZXR1cm4ocCkKfSkKCnBsb3RfbGlzdApgYGAKCiogdmlvbGluIHBsb3QsIHNwbGl0IGJ5IGNlbGwgdHlwZSwgc3BsaXQgYnkgbGFib3JhdG9yeQoKZm9yIG5vbiBtYXRyaXggY2VsbHMKCmBgYHtyIHZpb2xpbl9ub25fbWF0cml4X2NvbWJpbmVkLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gMTB9CmNhdGVnb3J5X29mX2ludGVyZXN0ID0gIm5vbiBtYXRyaXgiCgojIFNlbGVjdCBjZWxscyBvZiBpbnRlcmVzdApjZWxsX3R5cGVfb2ZfaW50ZXJlc3QgPSBhcy5jaGFyYWN0ZXIoY3VzdG9tX29yZGVyX2NlbGxfdHlwZVsKICB3aGljaChjdXN0b21fb3JkZXJfY2VsbF90eXBlJGNlbGxfY2F0ZWdvcnkgPT0gY2F0ZWdvcnlfb2ZfaW50ZXJlc3QpLCAiY2VsbF90eXBlIl0pCgpzdWJzb2JqX29mX2ludGVyZXN0ID0gc3Vic2V0KHNvYmosIGNlbGxfdHlwZSAlaW4lIGNlbGxfdHlwZV9vZl9pbnRlcmVzdCkKc3Vic29ial9vZl9pbnRlcmVzdCRjZWxsX3R5cGVfbGFibyA9IHBhc3RlMChzdWJzb2JqX29mX2ludGVyZXN0JGNlbGxfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzb2JqX29mX2ludGVyZXN0JGxhYm9yYXRvcnkpCgpjZWxsX21hcmtlcnNfb2ZfaW50ZXJlc3QgPSBjZWxsX21hcmtlcnNbY2VsbF90eXBlX29mX2ludGVyZXN0XQoKdG90YWxfaGVpZ2h0ID0gbWF4KGxlbmd0aHMoY2VsbF9tYXJrZXJzX29mX2ludGVyZXN0KSkgKyAxCgojIFZpb2xpbiBwbG90CnBhdGNod29ya19saXN0ID0gbGFwcGx5KG5hbWVzKGNlbGxfbWFya2Vyc19vZl9pbnRlcmVzdCksIEZVTiA9IGZ1bmN0aW9uKGNlbGxfdHlwZSkgewogIG1hcmtlcnNfc2V0ID0gY2VsbF9tYXJrZXJzX29mX2ludGVyZXN0W1tjZWxsX3R5cGVdXQogIAogICMgSW5kaXZpZHVhbCB2aW9saW4gcGxvdAogIHN1Yl9wbG90X2xpc3QgPSBsYXBwbHkobWFya2Vyc19zZXQsIEZVTiA9IGZ1bmN0aW9uKG9uZV9nZW5lKSB7CiAgICBwID0gU2V1cmF0OjpWbG5QbG90KHN1YnNvYmpfb2ZfaW50ZXJlc3QsCiAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gb25lX2dlbmUsIHB0LnNpemUgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjZWxsX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICBzcGxpdC5ieSA9ICJsYWJvcmF0b3J5IiwKICAgICAgICAgICAgICAgICAgICAgICAgY29scyA9IGxhYm9yYXRvcnlfY29sb3JzLAogICAgICAgICAgICAgICAgICAgICAgICBjb21iaW5lID0gRkFMU0UpW1sxXV0gKwogICAgICBnZ3Bsb3QyOjpsYWJzKHkgPSBvbmVfZ2VuZSkgKwogICAgICBnZ3Bsb3QyOjpzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLjAxLCAwKSkgKwogICAgICBnZ3Bsb3QyOjpnZW9tX3ZsaW5lKG1hcHBpbmcgPSBOVUxMLCBkYXRhID0gTlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgICB4aW50ZXJjZXB0ID0gYygxOjQpICsgMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9ICJncmF5NTAiLCBsdHkgPSAyKSArCiAgICAgIFNldXJhdDo6Tm9MZWdlbmQoKSArCiAgICAgIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgIyAiSWRlbnRpdHkiCiAgICAgICAgICAgICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9ibGFuaygpLCAgIyByZXBsYWNlZCBieSBwYW5lbC5ib3JkZXIKICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBhbmdsZSA9IDAsIHZqdXN0ID0gMC41KSwgIyBnZW5lCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLCAjICJFeHByZXNzaW9uIGxldmVsIgogICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwgIyBjZWxsIHR5cGUKICAgICAgICAgICAgICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMCwwLDAsMCksICJjbSIpLAogICAgICAgICAgICAgICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3Qoc2l6ZSA9IDEsIGNvbG9yID0gImdyYXk1MCIpKQogICAgcmV0dXJuKHApCiAgfSkKICAKICAjIFJlc2V0IGF4aXMudGV4dCBmb3IgbGFzdCBwbG90CiAgIyBzdWJfcGxvdF9saXN0W1tsZW5ndGgoc3ViX3Bsb3RfbGlzdCldXSA9IHN1Yl9wbG90X2xpc3RbW2xlbmd0aChzdWJfcGxvdF9saXN0KV1dICsKICAjICAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUpKSAjIGNlbGwgdHlwZQogIAogICMgQWRkIGVtcHR5IHBsb3QgdG8gYXJyYW5nZSBoZWlnaHRzCiAgc3ViX3Bsb3RfbGlzdFtbbGVuZ3RoKHN1Yl9wbG90X2xpc3QpICsgMV1dID0gcGF0Y2h3b3JrOjpwbG90X3NwYWNlcigpCiAgZW1wdHlfcGxvdF9oZWlnaHQgPSB0b3RhbF9oZWlnaHQgLSBsZW5ndGgobWFya2Vyc19zZXQpCiAgCiAgIyBBcnJhbmdlIGFzIHBhdGNod29yawogIHBwID0gcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHN1Yl9wbG90X2xpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0cyA9IGMocmVwKDEsIGxlbmd0aChtYXJrZXJzX3NldCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVtcHR5X3Bsb3RfaGVpZ2h0KSkgKwogICAgcGF0Y2h3b3JrOjpwbG90X2Fubm90YXRpb24odGl0bGUgPSBjZWxsX3R5cGUpCiAgCiAgcmV0dXJuKHBwKQp9KQpuYW1lcyhwYXRjaHdvcmtfbGlzdCkgPSBuYW1lcyhjZWxsX21hcmtlcnNfb2ZfaW50ZXJlc3QpCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGF0Y2h3b3JrX2xpc3QsIG5jb2wgPSAxKQpgYGAKCmZvciBtYXRyaXggY2VsbHMKCmBgYHtyIHZpb2xpbl9tYXRyaXhfY29tYmluZWQsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSAxMX0KY2F0ZWdvcnlfb2ZfaW50ZXJlc3QgPSAibWF0cml4IgoKIyBTZWxlY3QgY2VsbHMgb2YgaW50ZXJlc3QKY2VsbF90eXBlX29mX2ludGVyZXN0ID0gYXMuY2hhcmFjdGVyKGN1c3RvbV9vcmRlcl9jZWxsX3R5cGVbCiAgd2hpY2goY3VzdG9tX29yZGVyX2NlbGxfdHlwZSRjZWxsX2NhdGVnb3J5ID09IGNhdGVnb3J5X29mX2ludGVyZXN0KSwgImNlbGxfdHlwZSJdKQoKc3Vic29ial9vZl9pbnRlcmVzdCA9IHN1YnNldChzb2JqLCBjZWxsX3R5cGUgJWluJSBjZWxsX3R5cGVfb2ZfaW50ZXJlc3QpCnN1YnNvYmpfb2ZfaW50ZXJlc3QkY2VsbF90eXBlX2xhYm8gPSBwYXN0ZTAoc3Vic29ial9vZl9pbnRlcmVzdCRjZWxsX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic29ial9vZl9pbnRlcmVzdCRsYWJvcmF0b3J5KQoKY2VsbF9tYXJrZXJzX29mX2ludGVyZXN0ID0gY2VsbF9tYXJrZXJzW2NlbGxfdHlwZV9vZl9pbnRlcmVzdF0KCnRvdGFsX2hlaWdodCA9IG1heChsZW5ndGhzKGNlbGxfbWFya2Vyc19vZl9pbnRlcmVzdCkpICsgMQoKIyBWaW9saW4gcGxvdApwYXRjaHdvcmtfbGlzdCA9IGxhcHBseShuYW1lcyhjZWxsX21hcmtlcnNfb2ZfaW50ZXJlc3QpLCBGVU4gPSBmdW5jdGlvbihjZWxsX3R5cGUpIHsKICBtYXJrZXJzX3NldCA9IGNlbGxfbWFya2Vyc19vZl9pbnRlcmVzdFtbY2VsbF90eXBlXV0KICAKICAjIEluZGl2aWR1YWwgdmlvbGluIHBsb3QKICBzdWJfcGxvdF9saXN0ID0gbGFwcGx5KG1hcmtlcnNfc2V0LCBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogICAgcCA9IFNldXJhdDo6VmxuUGxvdChzdWJzb2JqX29mX2ludGVyZXN0LAogICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IG9uZV9nZW5lLCBwdC5zaXplID0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2VsbF90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXQuYnkgPSAibGFib3JhdG9yeSIsCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbHMgPSBsYWJvcmF0b3J5X2NvbG9ycywKICAgICAgICAgICAgICAgICAgICAgICAgY29tYmluZSA9IEZBTFNFKVtbMV1dICsKICAgICAgZ2dwbG90Mjo6bGFicyh5ID0gb25lX2dlbmUpICsKICAgICAgZ2dwbG90Mjo6c2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMC4wMSwgMCkpICsKICAgICAgZ2dwbG90Mjo6Z2VvbV92bGluZShtYXBwaW5nID0gTlVMTCwgZGF0YSA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeGludGVyY2VwdCA9IGMoMTo0KSArIDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSAiZ3JheTUwIiwgbHR5ID0gMikgKwogICAgICBTZXVyYXQ6Ok5vTGVnZW5kKCkgKwogICAgICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksICMgIklkZW50aXR5IgogICAgICAgICAgICAgICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfYmxhbmsoKSwgICMgcmVwbGFjZWQgYnkgcGFuZWwuYm9yZGVyCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgYW5nbGUgPSAwLCB2anVzdCA9IDAuNSksICMgZ2VuZQogICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgIyAiRXhwcmVzc2lvbiBsZXZlbCIKICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksICMgY2VsbCB0eXBlCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDAsMCwwLDApLCAiY20iKSwKICAgICAgICAgICAgICAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KHNpemUgPSAxLCBjb2xvciA9ICJncmF5NTAiKSkKICAgIHJldHVybihwKQogIH0pCiAgCiAgIyBSZXNldCBheGlzLnRleHQgZm9yIGxhc3QgcGxvdAogICMgc3ViX3Bsb3RfbGlzdFtbbGVuZ3RoKHN1Yl9wbG90X2xpc3QpXV0gPSBzdWJfcGxvdF9saXN0W1tsZW5ndGgoc3ViX3Bsb3RfbGlzdCldXSArCiAgIyAgIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41KSkgIyBjZWxsIHR5cGUKICAKICAjIEFkZCBlbXB0eSBwbG90IHRvIGFycmFuZ2UgaGVpZ2h0cwogIHN1Yl9wbG90X2xpc3RbW2xlbmd0aChzdWJfcGxvdF9saXN0KSArIDFdXSA9IHBhdGNod29yazo6cGxvdF9zcGFjZXIoKQogIGVtcHR5X3Bsb3RfaGVpZ2h0ID0gdG90YWxfaGVpZ2h0IC0gbGVuZ3RoKG1hcmtlcnNfc2V0KQogIAogICMgQXJyYW5nZSBhcyBwYXRjaHdvcmsKICBwcCA9IHBhdGNod29yazo6d3JhcF9wbG90cyhzdWJfcGxvdF9saXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlaWdodHMgPSBjKHJlcCgxLCBsZW5ndGgobWFya2Vyc19zZXQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbXB0eV9wbG90X2hlaWdodCkpICsKICAgIHBhdGNod29yazo6cGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gY2VsbF90eXBlKQogIAogIHJldHVybihwcCkKfSkKbmFtZXMocGF0Y2h3b3JrX2xpc3QpID0gbmFtZXMoY2VsbF9tYXJrZXJzX29mX2ludGVyZXN0KQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBhdGNod29ya19saXN0LCBuY29sID0gMSkKYGBgCgojIyBOb24tbWF0cml4IGNlbGxzCgpXZSBzZWxlY3QgdGhlIGRhdGFzZXRzOgoKYGBge3Igbm9uX21hdHJpeF9kYXRhc2V0X291cn0Kc29ial9hdGxhcyA9IGxpc3RfZGF0YSRvdXJfYXRsYXMkc29iagpuYW1lMkRfYXRsYXMgPSBsaXN0X2RhdGEkb3VyX2F0bGFzJG5hbWUyRApzb2JqID0gbGlzdF9kYXRhJG5vbl9tYXRyaXgkc29iagpuYW1lMkQgPSBsaXN0X2RhdGEkbm9uX21hdHJpeCRuYW1lMkQKbXlfdHJhaiA9IGxpc3RfZGF0YSRub25fbWF0cml4JG15X3RyYWoKCmNlbGxfdHlwZV9vZl9pbnRlcmVzdCA9IGMoIkhGLVNDcyIsICJPUlMiLCAiSUZFIGJhc2FsIiwgIklGRSBncmFudWxhciBzcGlub3VzIikKYGBgCgpMb2NhdGlvbiBvZiBjZWxscyBvbiB0aGUgd2hvbGUgYXRsYXM6CgpgYGB7ciBub25fbWF0cml4X2xvY2F0aW9uX291ciwgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDJ9CnNvYmpfYXRsYXMkY2VsbF9iYyA9IGNvbG5hbWVzKHNvYmpfYXRsYXMpCnNvYmokY2VsbF9iYyA9IGNvbG5hbWVzKHNvYmopCnNvYmpfYXRsYXMkaXNfb2ZfaW50ZXJlc3QgPSBkcGx5cjo6bGVmdF9qb2luKHNvYmpfYXRsYXNAbWV0YS5kYXRhWywgYygiY2VsbF9iYyIsICJwZXJjZW50Lm10IildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqQG1ldGEuZGF0YVssIGMoImNlbGxfYmMiLCAiY2x1c3Rlcl90eXBlIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJjZWxsX2JjIilbLCAiY2x1c3Rlcl90eXBlIl0KClNldXJhdDo6RGltUGxvdChzb2JqX2F0bGFzLCByZWR1Y3Rpb24gPSBuYW1lMkRfYXRsYXMsIHB0LnNpemUgPSAwLjAwMDAwMSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImlzX29mX2ludGVyZXN0Iiwgb3JkZXIgPSAiVFJVRSIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhjb2xvcl9tYXJrZXJzW2NlbGxfdHlwZV9vZl9pbnRlcmVzdF0sIGJnX2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYyhjZWxsX3R5cGVfb2ZfaW50ZXJlc3QsIE5BKSwgbmEudmFsdWUgPSBiZ19jb2xvcikgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgoqIHNhbXBsZSB0eXBlCgpgYGB7ciBzYW1wbGVfdHlwZV9ub25fbWF0cml4X291ciwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJzYW1wbGVfdHlwZSIsIGNvbHMgPSBzYW1wbGVfdHlwZV9jb2xvcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKKiBjZWxsIHR5cGUgYW5ub3RhdGlvbgoKYGBge3IgY2VsbF90eXBlX25vbl9tYXRyaXhfb3VyLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCiogY2x1c3RlciB0eXBlIGFubm90YXRpb24KCmBgYHtyIGNsdXN0ZXJfdHlwZV9ub25fbWF0cml4X291ciwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjbHVzdGVyX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgoqIHBzZXVkb3RpbWUKCmBgYHtyIHBzZXVkb3RpbWVfbm9uX21hdHJpeF9vdXIsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA1fQpTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJwc2V1ZG90aW1lIikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSB2aXJpZGlzOjp2aXJpZGlzKG4gPSAxMDApKSArCiAgZ2dwbG90Mjo6bGltcyh4ID0gcmFuZ2Uoc29iakByZWR1Y3Rpb25zW1tuYW1lMkRdXUBjZWxsLmVtYmVkZGluZ3NbLCAxXSksCiAgICAgICAgICAgICAgICB5ID0gcmFuZ2Uoc29iakByZWR1Y3Rpb25zW1tuYW1lMkRdXUBjZWxsLmVtYmVkZGluZ3NbLCAyXSkpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikgKwogIFNldXJhdDo6Tm9BeGVzKCkKYGBgCgoqIHBzZXVkb3RpbWUgd2l0aCBkeW5wbG90J3MgZnVuY3Rpb24gOgoKYGBge3IgcHNldWRvdGltZTJfbm9uX21hdHJpeF9vdXIsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpkeW5wbG90OjpwbG90X2RpbXJlZCh0cmFqZWN0b3J5ID0gbXlfdHJhaiwKICAgICAgICAgICAgICAgICAgICAgZGltcmVkID0gc29ialtbbmFtZTJEXV1AY2VsbC5lbWJlZGRpbmdzLAogICAgICAgICAgICAgICAgICAgICAjIENlbGxzCiAgICAgICAgICAgICAgICAgICAgIGNvbG9yX2NlbGxzID0gJ3BzZXVkb3RpbWUnLAogICAgICAgICAgICAgICAgICAgICBzaXplX2NlbGxzID0gMC41LAogICAgICAgICAgICAgICAgICAgICBib3JkZXJfcmFkaXVzX3BlcmNlbnRhZ2UgPSAwLAogICAgICAgICAgICAgICAgICAgICAjIFRyYWplY3RvcnkKICAgICAgICAgICAgICAgICAgICAgcGxvdF90cmFqZWN0b3J5ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgY29sb3JfdHJhamVjdG9yeSA9ICJub25lIiwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxfbWlsZXN0b25lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICBzaXplX21pbGVzdG9uZXMgPSAwLAogICAgICAgICAgICAgICAgICAgICBzaXplX3RyYW5zaXRpb25zID0gMSkKYGBgCgoqIHZpb2xpbiBwbG90cwoKYGBge3Igbm9uX21hdHJpeF9jb21tb25fZ2VuZXNfdmxuLCBmaWcud2lkdGggPSAyLjUsIGZpZy5oZWlnaHQgPSA0fQpmZWF0dXJlc19vaSA9IGMoIlNPWDkiLCAiVENGNCIsICJUQlgxIiwgIk5GQVRDMSIsIAogICAgICAgICAgICAgICAgIkxIWDIiLCAiUlVOWDEiLCAiVklNIiwgIlRDRjMiLCAiRkdGMTgiLCAiQ0QzNCIsCiAgICAgICAgICAgICAgICAjIEhGLVNDcyArIElGRSBiYXNhbAogICAgICAgICAgICAgICAgIkNPTDE3QTEiLCAiTVQyQSIsICJUWE5JUCIsICJDQVZJTjEiLAogICAgICAgICAgICAgICAgIyBIRi1TQ3MgKyBPUlMKICAgICAgICAgICAgICAgICJUVUJCMkEiLCAiS1JUNkIiLCAiTUFSQ0tTTDEiLCAiSUQ0IiwgIkNZUDFCMSIsICJCQVJYMiIsCiAgICAgICAgICAgICAgICAjIEhGLVNDcwogICAgICAgICAgICAgICAgIkxNQ0QxIiwgIlRDRUFMMiIsICJGUlpCIiwgIlRIQlMxIiwgIkRJTzIiKQoKcGxvdF9saXN0ID0gbGFwcGx5KGZlYXR1cmVzX29pLCBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIHAgPSBTZXVyYXQ6OlZsblBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gb25lX2dlbmUsIHB0LnNpemUgPSAwLjAwMSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JfbWFya2Vyc1tsZXZlbHMoc29iaiRjbHVzdGVyX3R5cGUpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGxldmVscyhzb2JqJGNsdXN0ZXJfdHlwZSkpICsKICAgIGdncGxvdDI6OnRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQogIHJldHVybihwKQp9KQoKbmFtZXMocGxvdF9saXN0KSA9IGZlYXR1cmVzX29pCnBsb3RfbGlzdApgYGAKCiogZmVhdHVyZSBwbG90cwoKYGBge3Igbm9uX21hdHJpeF9jb21tb25fZ2VuZXNfZmVhdHVyZSwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9CnBsb3RfbGlzdCA9IGxhcHBseShmZWF0dXJlc19vaSwgRlVOID0gZnVuY3Rpb24ob25lX2dlbmUpIHsKICBwID0gU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCBmZWF0dXJlcyA9IG9uZV9nZW5lLCByZWR1Y3Rpb24gPSBuYW1lMkQpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSkgKwogICAgZ2dwbG90Mjo6bGltcyh4ID0gcmFuZ2Uoc29iakByZWR1Y3Rpb25zW1tuYW1lMkRdXUBjZWxsLmVtYmVkZGluZ3NbLCAxXSksCiAgICAgICAgICAgICAgICAgIHkgPSByYW5nZShzb2JqQHJlZHVjdGlvbnNbW25hbWUyRF1dQGNlbGwuZW1iZWRkaW5nc1ssIDJdKSkgKwogICAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogICAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgICBTZXVyYXQ6Ok5vTGVnZW5kKCkKICAKICByZXR1cm4ocCkKfSkKCm5hbWVzKHBsb3RfbGlzdCkgPSBmZWF0dXJlc19vaQpwbG90X2xpc3QKYGBgCgojIyBPUlMgdnMgSUZFIGJhc2FsCgojIyMgT3VyIGRhdGFzZXQKCldlIHNlbGVjdCB0aGUgZGF0YXNldDoKCmBgYHtyIG9yc19pZmViX291cn0Kc29ial9hdGxhcyA9IGxpc3RfZGF0YSRvdXJfYXRsYXMkc29iagpuYW1lMkRfYXRsYXMgPSBsaXN0X2RhdGEkb3VyX2F0bGFzJG5hbWUyRApzb2JqID0gbGlzdF9kYXRhJG9yc19pZmViJHNvYmoKbmFtZTJEID0gbGlzdF9kYXRhJG9yc19pZmViJG5hbWUyRApsaXN0X3Jlc3VsdHMgPSBsaXN0X2RhdGEkb3JzX2lmZWIkbGlzdF9yZXN1bHRzCmBgYAoKTG9jYXRpb24gb2YgY2VsbHMgb24gdGhlIHdob2xlIGF0bGFzOgoKYGBge3Igb3JzX2lmZWJfbG9jYXRpb25fb3VyLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMn0Kc29ial9hdGxhcyRjZWxsX2JjID0gY29sbmFtZXMoc29ial9hdGxhcykKc29iaiRjZWxsX2JjID0gY29sbmFtZXMoc29iaikKc29ial9hdGxhcyRpc19vZl9pbnRlcmVzdCA9IGRwbHlyOjpsZWZ0X2pvaW4oc29ial9hdGxhc0BtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgInBlcmNlbnQubXQiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvYmpAbWV0YS5kYXRhWywgYygiY2VsbF9iYyIsICJjZWxsX3R5cGUiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gImNlbGxfYmMiKVssICJjZWxsX3R5cGUiXQoKU2V1cmF0OjpEaW1QbG90KHNvYmpfYXRsYXMsIHJlZHVjdGlvbiA9IG5hbWUyRF9hdGxhcywgcHQuc2l6ZSA9IDAuMDAwMDAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiaXNfb2ZfaW50ZXJlc3QiLCBvcmRlciA9ICJUUlVFIikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKGNvbG9yX21hcmtlcnNbY2VsbF90eXBlX29mX2ludGVyZXN0XSwgYmdfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKGNlbGxfdHlwZV9vZl9pbnRlcmVzdCwgTkEpLCBuYS52YWx1ZSA9IGJnX2NvbG9yKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkRFIGdlbmVzIGJldHdlZW4gT1JTIGFuZCBJRkUgYmFzYWwgOgoKYGBge3IgZGVfb3JzX2lmZWJfb3VyLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNn0KbWFyayA9IGxpc3RfcmVzdWx0cyRPUlNfdnNfSUZFX2Jhc2FsJG1hcmsKbWFyayRnZW5lX25hbWUgPSByb3duYW1lcyhtYXJrKQptYXJrX2xhYmVsID0gcmJpbmQoCiAgIyB1cC1yZWd1bGF0ZWQgaW4gT1JTCiAgbWFyayAlPiUgZHBseXI6OnRvcF9uKC4sIG4gPSAyMCwgd3QgPSBhdmdfbG9nRkMpLAogICMgdXAtcmVndWxhdGVkIGluIElGRSBiYXNhbAogIG1hcmsgJT4lIGRwbHlyOjp0b3BfbiguLCBuID0gMjAsIHd0ID0gLWF2Z19sb2dGQyksCiAgIyByZXByZXNlbnRhdGl2ZSBhbmQgc2VsZWN0aXZlIGZvciBPUlMKICBtYXJrICU+JSBkcGx5cjo6dG9wX24oLiwgbiA9IDIwLCB3dCA9IChwY3QuMSAtIHBjdC4yKSksCiAgIyByZXByZXNlbnRhdGl2ZSBhbmQgc2VsZWN0aXZlIGZvciBJRkUgYmFzYWwKICBtYXJrICU+JSBkcGx5cjo6dG9wX24oLiwgbiA9IDIwLCB3dCA9IC0ocGN0LjEgLSBwY3QuMikpKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QoKQptYXJrX2xhYmVsID0gbWFya19sYWJlbFshZ3JlcGwocm93bmFtZXMobWFya19sYWJlbCksIHBhdHRlcm4gPSAiXk1UIiksIF0KCmF2Z19sb2dGQ19yYW5nZSA9IHNldE5hbWVzKGMobWluKG1hcmtfbGFiZWwkYXZnX2xvZ0ZDKSwgLTEsIDAsIDEsIG1heChtYXJrX2xhYmVsJGF2Z19sb2dGQykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBubSA9IGMoImRvZGdlcmJsdWU0IiwgImRvZGdlcmJsdWUzIiwgIiNCN0I3QjciLCAiZmlyZWJyaWNrMyIsICJmaXJlYnJpY2s0IikpCgoKZ2dwbG90Mjo6Z2dwbG90KG1hcmssIGFlcyh4ID0gcGN0LjEsIHkgPSBwY3QuMiwgY29sID0gYXZnX2xvZ0ZDKSkgKwogIGdncGxvdDI6Omdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgbHR5ID0gMikgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gbWFya19sYWJlbCwgbWF4Lm92ZXJsYXBzID0gSW5mLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBwY3QuMSwgeSA9IHBjdC4yLCBsYWJlbCA9IGdlbmVfbmFtZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSAiYmxhY2siLCBmaWxsID0gTkEsIHNpemUgPSAzLjUsIGxhYmVsLnNpemUgPSBOQSkgKwogIGdncGxvdDI6OmxhYnMoeCA9ICJFbnJpY2hlZCBpbiBPUlMiLAogICAgICAgICAgICAgICAgeSA9ICJFbnJpY2hlZCBpbiBJRkUgYmFzYWwiKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IG5hbWVzKGF2Z19sb2dGQ19yYW5nZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IHNjYWxlczo6cmVzY2FsZSh1bm5hbWUoYXZnX2xvZ0ZDX3JhbmdlKSkpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCmBgYAoKKiBHU0VBLCBlbnJpY2hlZCBpbiBPUlMgY29tcGFyZWQgdG8gSUZFIGJhc2FsCgpgYGB7ciBrZXJhX29yc19pZmViX291ciwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9CnRoZV9nc19uYW1lID0gIlJFQUNUT01FX0tFUkFUSU5JWkFUSU9OIiAKdGhlX2NvbnRlbnQgPSBsaXN0X3Jlc3VsdHMkT1JTX3ZzX0lGRV9iYXNhbCRnc2VhQHJlc3VsdCAlPiUKICBkcGx5cjo6ZmlsdGVyKElEID09IHRoZV9nc19uYW1lKQp0aGVfc3VidGl0bGUgPSBwYXN0ZTAoIlxuTkVTIDogIiwgcm91bmQodGhlX2NvbnRlbnQkTkVTLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICJcdHxcdHB2YWx1ZSA6ICIsIHJvdW5kKHRoZV9jb250ZW50JHB2YWx1ZSwgNCksCiAgICAgICAgICAgICAgICAgICAgICAiXHR8XHRzZXQgc2l6ZSA6ICIsIHRoZV9jb250ZW50JHNldFNpemUsICIgZ2VuZXMiKQoKZW5yaWNocGxvdDo6Z3NlYXBsb3QyKHggPSBsaXN0X3Jlc3VsdHMkT1JTX3ZzX0lGRV9iYXNhbCRnc2VhLAogICAgICAgICAgICAgICAgICAgICAgZ2VuZVNldElEID0gdGhlX2dzX25hbWUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gdGhlX2dzX25hbWUsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHRoZV9zdWJ0aXRsZSkgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBnZ3Bsb3QyOjptYXJnaW4oMywgMywgNSwgMykpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBnZ3RleHQ6OmVsZW1lbnRfbWFya2Rvd24oaGp1c3QgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTApKQpgYGAKCiogR1NFQSwgZW5yaWNoZWQgaW4gSUZFIGJhc2FsIGNvbXBhcmVkIHRvIE9SUwoKYGBge3IgaWZuX29yc19pZmViX291ciwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9CnRoZV9nc19uYW1lID0gIkhBTExNQVJLX0lOVEVSRkVST05fR0FNTUFfUkVTUE9OU0UiIAp0aGVfY29udGVudCA9IGxpc3RfcmVzdWx0cyRPUlNfdnNfSUZFX2Jhc2FsJGdzZWFAcmVzdWx0ICU+JQogIGRwbHlyOjpmaWx0ZXIoSUQgPT0gdGhlX2dzX25hbWUpCnRoZV9zdWJ0aXRsZSA9IHBhc3RlMCgiXG5ORVMgOiAiLCByb3VuZCh0aGVfY29udGVudCRORVMsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIlx0fFx0cHZhbHVlIDogIiwgcm91bmQodGhlX2NvbnRlbnQkcHZhbHVlLCA0KSwKICAgICAgICAgICAgICAgICAgICAgICJcdHxcdHNldCBzaXplIDogIiwgdGhlX2NvbnRlbnQkc2V0U2l6ZSwgIiBnZW5lcyIpCgplbnJpY2hwbG90Ojpnc2VhcGxvdDIoeCA9IGxpc3RfcmVzdWx0cyRPUlNfdnNfSUZFX2Jhc2FsJGdzZWEsCiAgICAgICAgICAgICAgICAgICAgICBnZW5lU2V0SUQgPSB0aGVfZ3NfbmFtZSkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSB0aGVfZ3NfbmFtZSwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gdGhlX3N1YnRpdGxlKSArCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbiA9IGdncGxvdDI6Om1hcmdpbigzLCAzLCA1LCAzKSksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGdndGV4dDo6ZWxlbWVudF9tYXJrZG93bihoanVzdCA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMCkpCmBgYAoKCiogdmlvbGluIHBsb3RzIG9mIERFIGdlbmVzIGluIE9SUywgYmV0d2VlbiBIUyBhbmQgSEQKCmBgYHtyIG9yc19pZmViX3Zsbl9vcnNfb3VyLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gM30Kc3Vic29iaiA9IHN1YnNldChzb2JqLCBjZWxsX3R5cGUgPT0gIk9SUyIpCmZlYXR1cmVzX29pID0gYygiRFVTUDEiLCAiRERJVDQiLCAiR0FCQVJBUCIsICJaTkYzMDIiLAogICAgICAgICAgICAgICAgIk1JRiIsICJMR0FMUzciLCAiQVJGNSIsICJTMTAwQTkiKQoKIyBQbG90ICEKcGxvdF9saXN0ID0gbGFwcGx5KGZlYXR1cmVzX29pLCBGVU4gPSBmdW5jdGlvbihmZWF0dXJlKSB7CiAgIyB0LXRlc3QgYmV0d2VlbiBzYW1wbGUgdHlwZQogIGZlYXR1cmVfZXhwciA9IHN1YnNvYmpAYXNzYXlzJFJOQUBkYXRhW2ZlYXR1cmUsIF0KICBmZWF0dXJlX2hzID0gZmVhdHVyZV9leHByW3N1YnNvYmokc2FtcGxlX3R5cGUgPT0gIkhTIl0KICBmZWF0dXJlX2hkID0gZmVhdHVyZV9leHByW3N1YnNvYmokc2FtcGxlX3R5cGUgPT0gIkhEIl0KICBmZWF0dXJlX2hzX1ZTX2ZlYXR1cmVfaGQgPSBzdGF0czo6dC50ZXN0KGZlYXR1cmVfaHMsIGZlYXR1cmVfaGQpCiAgZmVhdHVyZV9oc19WU19mZWF0dXJlX2hkCiAgCiAgIyBTaWduaWZpY2FuY2UgYXNzb2NpYXRlZCB3aXRoIHAtdmFsdWUKICBwdmFsID0gZmVhdHVyZV9oc19WU19mZWF0dXJlX2hkJHAudmFsdWUKICAKICBzaWduaWZpY2FuY2UgPSBjYXNlX3doZW4ocHZhbCA+IDAuMDUgfiAibnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsID4gMC4wMSAmIHB2YWwgPD0gMC4wNSB+ICIqIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbCA+IDAuMDAxICYgcHZhbCA8PSAwLjAxIH4gIioqIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbCA8PSAwLjAwMSB+ICIqKioiKQogIAogICMgUGxvdAogIHAgPSBTZXVyYXQ6OlZsblBsb3Qoc3Vic29iaiwgZ3JvdXAuYnkgPSAic2FtcGxlX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgcHQuc2l6ZSA9IDAuMywgZmVhdHVyZXMgPSBmZWF0dXJlKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBzYW1wbGVfdHlwZV9jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhzYW1wbGVfdHlwZV9jb2xvcnMpKSArCiAgICAjIFNpZ25pZmljYW5jZSBiYXIKICAgIGdncGxvdDI6Omdlb21fc2VnbWVudChkYXRhID0gZGF0YS5mcmFtZSgxKSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAxLCB4ZW5kID0gMiwKICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbWF4KGZlYXR1cmVfZXhwcikrMC4zLCB5ZW5kID0gbWF4KGZlYXR1cmVfZXhwcikrMC4zKSArCiAgICBnZ3Bsb3QyOjpnZW9tX2xhYmVsKGRhdGEgPSBkYXRhLmZyYW1lKDEpLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICB4ID0gMS41LCB5ID0gbWF4KGZlYXR1cmVfZXhwcikrMC4zNSwKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBzaWduaWZpY2FuY2UsCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBOQSwgbGFiZWwuc2l6ZSA9IDApICsKICAgIGdncGxvdDI6OmxpbXMoeSA9IGMoMCwgbWF4KGZlYXR1cmVfZXhwcikrMC40KSkgKwogICAgIyBUaGVtZQogICAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQogIAogIHJldHVybihwKQp9KQoKbmFtZXMocGxvdF9saXN0KSA9IGZlYXR1cmVzX29pCnBsb3RfbGlzdApgYGAKCiogdmlvbGluIHBsb3RzIG9mIERFIGdlbmVzIGluIElGRSBiYXNhbCwgYmV0d2VlbiBIUyBhbmQgSEQKCmBgYHtyIG9yc19pZmViX3Zsbl9pZmViX291ciwgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDN9CnN1YnNvYmogPSBzdWJzZXQoc29iaiwgY2VsbF90eXBlID09ICJJRkUgYmFzYWwiKQpmZWF0dXJlc19vaSA9IGMoIkRVU1AxIiwgIktMRjYiLCAiQ0xETjEiLCAiQ1RHRiIsCiAgICAgICAgICAgICAgICAiSUZJMjciLCAiSUZJVE0zIiwgIkNDTDIiLCAiUzEwMEE5IikKCiMgUGxvdCAhCnBsb3RfbGlzdCA9IGxhcHBseShmZWF0dXJlc19vaSwgRlVOID0gZnVuY3Rpb24oZmVhdHVyZSkgewogICMgdC10ZXN0IGJldHdlZW4gc2FtcGxlIHR5cGUKICBmZWF0dXJlX2V4cHIgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVtmZWF0dXJlLCBdCiAgZmVhdHVyZV9ocyA9IGZlYXR1cmVfZXhwcltzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIUyJdCiAgZmVhdHVyZV9oZCA9IGZlYXR1cmVfZXhwcltzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIRCJdCiAgZmVhdHVyZV9oc19WU19mZWF0dXJlX2hkID0gc3RhdHM6OnQudGVzdChmZWF0dXJlX2hzLCBmZWF0dXJlX2hkKQogIGZlYXR1cmVfaHNfVlNfZmVhdHVyZV9oZAogIAogICMgU2lnbmlmaWNhbmNlIGFzc29jaWF0ZWQgd2l0aCBwLXZhbHVlCiAgcHZhbCA9IGZlYXR1cmVfaHNfVlNfZmVhdHVyZV9oZCRwLnZhbHVlCiAgCiAgc2lnbmlmaWNhbmNlID0gY2FzZV93aGVuKHB2YWwgPiAwLjA1IH4gIm5zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbCA+IDAuMDEgJiBwdmFsIDw9IDAuMDUgfiAiKiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWwgPiAwLjAwMSAmIHB2YWwgPD0gMC4wMSB+ICIqKiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWwgPD0gMC4wMDEgfiAiKioqIikKICAKICAjIFBsb3QKICBwID0gU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgIHB0LnNpemUgPSAwLjMsIGZlYXR1cmVzID0gZmVhdHVyZSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gc2FtcGxlX3R5cGVfY29sb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gbmFtZXMoc2FtcGxlX3R5cGVfY29sb3JzKSkgKwogICAgIyBTaWduaWZpY2FuY2UgYmFyCiAgICBnZ3Bsb3QyOjpnZW9tX3NlZ21lbnQoZGF0YSA9IGRhdGEuZnJhbWUoMSksIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gMSwgeGVuZCA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IG1heChmZWF0dXJlX2V4cHIpKzAuMywgeWVuZCA9IG1heChmZWF0dXJlX2V4cHIpKzAuMykgKwogICAgZ2dwbG90Mjo6Z2VvbV9sYWJlbChkYXRhID0gZGF0YS5mcmFtZSgxKSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgeCA9IDEuNSwgeSA9IG1heChmZWF0dXJlX2V4cHIpKzAuMzUsCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gc2lnbmlmaWNhbmNlLAogICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gTkEsIGxhYmVsLnNpemUgPSAwKSArCiAgICBnZ3Bsb3QyOjpsaW1zKHkgPSBjKDAsIG1heChmZWF0dXJlX2V4cHIpKzAuNCkpICsKICAgICMgVGhlbWUKICAgIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICAKICByZXR1cm4ocCkKfSkKCm5hbWVzKHBsb3RfbGlzdCkgPSBmZWF0dXJlc19vaQpwbG90X2xpc3QKYGBgCgoKIyMjIFd1IGRhdGFzZXQKCldlIHNlbGVjdCB0aGUgZGF0YXNldDoKCmBgYHtyIG9yc19pZmViX3d1fQpzb2JqX2F0bGFzID0gbGlzdF9kYXRhJHd1X2F0bGFzJHNvYmoKbmFtZTJEX2F0bGFzID0gbGlzdF9kYXRhJHd1X2F0bGFzJG5hbWUyRApzb2JqID0gbGlzdF9kYXRhJHd1X29yc19pZmViJHNvYmoKbmFtZTJEID0gbGlzdF9kYXRhJHd1X29yc19pZmViJG5hbWUyRApsaXN0X3Jlc3VsdHMgPSBsaXN0X2RhdGEkd3Vfb3JzX2lmZWIkbGlzdF9yZXN1bHRzCmBgYAoKTG9jYXRpb24gb2YgY2VsbHMgb24gdGhlIHdob2xlIGF0bGFzOgoKYGBge3Igb3JzX2lmZWJfbG9jYXRpb25fd3UsIGZpZy53aWR0aCA9IDIsIGZpZy5oZWlnaHQgPSAyfQpzb2JqX2F0bGFzJGNlbGxfYmMgPSBjb2xuYW1lcyhzb2JqX2F0bGFzKQpzb2JqJGNlbGxfYmMgPSBjb2xuYW1lcyhzb2JqKQpzb2JqX2F0bGFzJGlzX29mX2ludGVyZXN0ID0gZHBseXI6OmxlZnRfam9pbihzb2JqX2F0bGFzQG1ldGEuZGF0YVssIGMoImNlbGxfYmMiLCAicGVyY2VudC5tdCIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29iakBtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgImNlbGxfdHlwZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAiY2VsbF9iYyIpWywgImNlbGxfdHlwZSJdCgpTZXVyYXQ6OkRpbVBsb3Qoc29ial9hdGxhcywgcmVkdWN0aW9uID0gbmFtZTJEX2F0bGFzLCBwdC5zaXplID0gMC4wMDAwMDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJpc19vZl9pbnRlcmVzdCIsIG9yZGVyID0gIlRSVUUiKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoY29sb3JfbWFya2Vyc1tjZWxsX3R5cGVfb2ZfaW50ZXJlc3RdLCBiZ19jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoY2VsbF90eXBlX29mX2ludGVyZXN0LCBOQSksIG5hLnZhbHVlID0gYmdfY29sb3IpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKKiBHU0VBLCBlbnJpY2hlZCBpbiBPUlMgY29tcGFyZWQgdG8gSUZFIGJhc2FsCgpgYGB7ciBrZXJhX29yc19pZmViX3d1LCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KdGhlX2dzX25hbWUgPSAiUkVBQ1RPTUVfS0VSQVRJTklaQVRJT04iIAp0aGVfY29udGVudCA9IGxpc3RfcmVzdWx0cyRPUlNfdnNfSUZFX2Jhc2FsJGdzZWFAcmVzdWx0ICU+JQogIGRwbHlyOjpmaWx0ZXIoSUQgPT0gdGhlX2dzX25hbWUpCnRoZV9zdWJ0aXRsZSA9IHBhc3RlMCgiXG5ORVMgOiAiLCByb3VuZCh0aGVfY29udGVudCRORVMsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIlx0fFx0cHZhbHVlIDogIiwgcm91bmQodGhlX2NvbnRlbnQkcHZhbHVlLCA0KSwKICAgICAgICAgICAgICAgICAgICAgICJcdHxcdHNldCBzaXplIDogIiwgdGhlX2NvbnRlbnQkc2V0U2l6ZSwgIiBnZW5lcyIpCgplbnJpY2hwbG90Ojpnc2VhcGxvdDIoeCA9IGxpc3RfcmVzdWx0cyRPUlNfdnNfSUZFX2Jhc2FsJGdzZWEsCiAgICAgICAgICAgICAgICAgICAgICBnZW5lU2V0SUQgPSB0aGVfZ3NfbmFtZSkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSB0aGVfZ3NfbmFtZSwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gdGhlX3N1YnRpdGxlKSArCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbiA9IGdncGxvdDI6Om1hcmdpbigzLCAzLCA1LCAzKSksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGdndGV4dDo6ZWxlbWVudF9tYXJrZG93bihoanVzdCA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMCkpCmBgYAoKKiBHU0VBLCBlbnJpY2hlZCBpbiBJRkUgYmFzYWwgY29tcGFyZWQgdG8gT1JTCgpgYGB7ciBpZm5fb3JzX2lmZWJfd3UsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQp0aGVfZ3NfbmFtZSA9ICJIQUxMTUFSS19JTlRFUkZFUk9OX0dBTU1BX1JFU1BPTlNFIiAKdGhlX2NvbnRlbnQgPSBsaXN0X3Jlc3VsdHMkT1JTX3ZzX0lGRV9iYXNhbCRnc2VhQHJlc3VsdCAlPiUKICBkcGx5cjo6ZmlsdGVyKElEID09IHRoZV9nc19uYW1lKQp0aGVfc3VidGl0bGUgPSBwYXN0ZTAoIlxuTkVTIDogIiwgcm91bmQodGhlX2NvbnRlbnQkTkVTLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICJcdHxcdHB2YWx1ZSA6ICIsIHJvdW5kKHRoZV9jb250ZW50JHB2YWx1ZSwgNCksCiAgICAgICAgICAgICAgICAgICAgICAiXHR8XHRzZXQgc2l6ZSA6ICIsIHRoZV9jb250ZW50JHNldFNpemUsICIgZ2VuZXMiKQoKZW5yaWNocGxvdDo6Z3NlYXBsb3QyKHggPSBsaXN0X3Jlc3VsdHMkT1JTX3ZzX0lGRV9iYXNhbCRnc2VhLAogICAgICAgICAgICAgICAgICAgICAgZ2VuZVNldElEID0gdGhlX2dzX25hbWUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gdGhlX2dzX25hbWUsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHRoZV9zdWJ0aXRsZSkgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBnZ3Bsb3QyOjptYXJnaW4oMywgMywgNSwgMykpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBnZ3RleHQ6OmVsZW1lbnRfbWFya2Rvd24oaGp1c3QgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTApKQpgYGAKCiMjIyBUYWthaGFzaGkgZGF0YXNldAoKV2Ugc2VsZWN0IHRoZSBkYXRhc2V0OgoKYGBge3Igb3JzX2lmZWJfdGFrYWhhc2hpfQpzb2JqX2F0bGFzID0gbGlzdF9kYXRhJHRha2FoYXNoaV9hdGxhcyRzb2JqCm5hbWUyRF9hdGxhcyA9IGxpc3RfZGF0YSR0YWthaGFzaGlfYXRsYXMkbmFtZTJECnNvYmogPSBsaXN0X2RhdGEkdGFrYWhhc2hpX29yc19pZmViJHNvYmoKbmFtZTJEID0gbGlzdF9kYXRhJHRha2FoYXNoaV9vcnNfaWZlYiRuYW1lMkQKbGlzdF9yZXN1bHRzID0gbGlzdF9kYXRhJHRha2FoYXNoaV9vcnNfaWZlYiRsaXN0X3Jlc3VsdHMKYGBgCgpMb2NhdGlvbiBvZiBjZWxscyBvbiB0aGUgd2hvbGUgYXRsYXM6CgpgYGB7ciBvcnNfaWZlYl9sb2NhdGlvbl90YWthaGFzaGksIGZpZy53aWR0aCA9IDIsIGZpZy5oZWlnaHQgPSAyfQpzb2JqX2F0bGFzJGNlbGxfYmMgPSBjb2xuYW1lcyhzb2JqX2F0bGFzKQpzb2JqJGNlbGxfYmMgPSBjb2xuYW1lcyhzb2JqKQpzb2JqX2F0bGFzJGlzX29mX2ludGVyZXN0ID0gZHBseXI6OmxlZnRfam9pbihzb2JqX2F0bGFzQG1ldGEuZGF0YVssIGMoImNlbGxfYmMiLCAicGVyY2VudC5tdCIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29iakBtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgImNlbGxfdHlwZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAiY2VsbF9iYyIpWywgImNlbGxfdHlwZSJdCgpTZXVyYXQ6OkRpbVBsb3Qoc29ial9hdGxhcywgcmVkdWN0aW9uID0gbmFtZTJEX2F0bGFzLCBwdC5zaXplID0gMC4wMDAwMDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJpc19vZl9pbnRlcmVzdCIsIG9yZGVyID0gIlRSVUUiKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoY29sb3JfbWFya2Vyc1tjZWxsX3R5cGVfb2ZfaW50ZXJlc3RdLCBiZ19jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoY2VsbF90eXBlX29mX2ludGVyZXN0LCBOQSksIG5hLnZhbHVlID0gYmdfY29sb3IpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKKiBHU0VBLCBlbnJpY2hlZCBpbiBPUlMgY29tcGFyZWQgdG8gSUZFIGJhc2FsCgpgYGB7ciBrZXJhX29yc19pZmViX3Rha2FoYXNoaSwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9CnRoZV9nc19uYW1lID0gIlJFQUNUT01FX0tFUkFUSU5JWkFUSU9OIiAKdGhlX2NvbnRlbnQgPSBsaXN0X3Jlc3VsdHMkT1JTX3ZzX0lGRV9iYXNhbCRnc2VhQHJlc3VsdCAlPiUKICBkcGx5cjo6ZmlsdGVyKElEID09IHRoZV9nc19uYW1lKQp0aGVfc3VidGl0bGUgPSBwYXN0ZTAoIlxuTkVTIDogIiwgcm91bmQodGhlX2NvbnRlbnQkTkVTLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICJcdHxcdHB2YWx1ZSA6ICIsIHJvdW5kKHRoZV9jb250ZW50JHB2YWx1ZSwgNCksCiAgICAgICAgICAgICAgICAgICAgICAiXHR8XHRzZXQgc2l6ZSA6ICIsIHRoZV9jb250ZW50JHNldFNpemUsICIgZ2VuZXMiKQoKZW5yaWNocGxvdDo6Z3NlYXBsb3QyKHggPSBsaXN0X3Jlc3VsdHMkT1JTX3ZzX0lGRV9iYXNhbCRnc2VhLAogICAgICAgICAgICAgICAgICAgICAgZ2VuZVNldElEID0gdGhlX2dzX25hbWUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gdGhlX2dzX25hbWUsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHRoZV9zdWJ0aXRsZSkgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBnZ3Bsb3QyOjptYXJnaW4oMywgMywgNSwgMykpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBnZ3RleHQ6OmVsZW1lbnRfbWFya2Rvd24oaGp1c3QgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTApKQpgYGAKCiogR1NFQSwgZW5yaWNoZWQgaW4gSUZFIGJhc2FsIGNvbXBhcmVkIHRvIE9SUwoKYGBge3IgaWZuX29yc19pZmViX3Rha2FoYXNoaSwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9CnRoZV9nc19uYW1lID0gIkhBTExNQVJLX0lOVEVSRkVST05fR0FNTUFfUkVTUE9OU0UiIAp0aGVfY29udGVudCA9IGxpc3RfcmVzdWx0cyRPUlNfdnNfSUZFX2Jhc2FsJGdzZWFAcmVzdWx0ICU+JQogIGRwbHlyOjpmaWx0ZXIoSUQgPT0gdGhlX2dzX25hbWUpCnRoZV9zdWJ0aXRsZSA9IHBhc3RlMCgiXG5ORVMgOiAiLCByb3VuZCh0aGVfY29udGVudCRORVMsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIlx0fFx0cHZhbHVlIDogIiwgcm91bmQodGhlX2NvbnRlbnQkcHZhbHVlLCA0KSwKICAgICAgICAgICAgICAgICAgICAgICJcdHxcdHNldCBzaXplIDogIiwgdGhlX2NvbnRlbnQkc2V0U2l6ZSwgIiBnZW5lcyIpCgplbnJpY2hwbG90Ojpnc2VhcGxvdDIoeCA9IGxpc3RfcmVzdWx0cyRPUlNfdnNfSUZFX2Jhc2FsJGdzZWEsCiAgICAgICAgICAgICAgICAgICAgICBnZW5lU2V0SUQgPSB0aGVfZ3NfbmFtZSkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSB0aGVfZ3NfbmFtZSwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gdGhlX3N1YnRpdGxlKSArCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbiA9IGdncGxvdDI6Om1hcmdpbigzLCAzLCA1LCAzKSksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGdndGV4dDo6ZWxlbWVudF9tYXJrZG93bihoanVzdCA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMCkpCmBgYAoKIyMgSEYtU0NzCgpXZSBzZWxlY3QgdGhlIGRhdGFzZXQ6CgpgYGB7ciBoZnNjc19vdXJ9CnNvYmogPSBsaXN0X2RhdGEkaGZzYyRzb2JqCm5hbWUyRCA9IGxpc3RfZGF0YSRoZnNjJG5hbWUyRApsaXN0X3Jlc3VsdHMgPSBsaXN0X2RhdGEkaGZzYyRsaXN0X3Jlc3VsdHMKYGBgCgoqIHZpb2xpbiBwbG90cyBvZiBERSBnZW5lcyBpbiBIRi1TQ3MsIGJldHdlZW4gSFMgYW5kIEhECgpgYGB7ciBoZnNjc192bG5fb3JzX291ciwgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDN9CmZlYXR1cmVzX29pID0gYygiSExBLUMiLCAiSExBLUEiLCAiSUZJVE0zIiwgIk1JRiIsICJKVU5CIiwgIlBQSUEiLAogICAgICAgICAgICAgICAgIlBESzQiLCAiRERJVDQiLCAiQlRHMiIsICJUWE5JUCIsICJLTEY5IikKCiMgUGxvdCAhCnBsb3RfbGlzdCA9IGxhcHBseShmZWF0dXJlc19vaSwgRlVOID0gZnVuY3Rpb24oZmVhdHVyZSkgewogICMgdC10ZXN0IGJldHdlZW4gc2FtcGxlIHR5cGUKICBmZWF0dXJlX2V4cHIgPSBzb2JqQGFzc2F5cyRSTkFAZGF0YVtmZWF0dXJlLCBdCiAgZmVhdHVyZV9ocyA9IGZlYXR1cmVfZXhwcltzb2JqJHNhbXBsZV90eXBlID09ICJIUyJdCiAgZmVhdHVyZV9oZCA9IGZlYXR1cmVfZXhwcltzb2JqJHNhbXBsZV90eXBlID09ICJIRCJdCiAgZmVhdHVyZV9oc19WU19mZWF0dXJlX2hkID0gc3RhdHM6OnQudGVzdChmZWF0dXJlX2hzLCBmZWF0dXJlX2hkKQogIGZlYXR1cmVfaHNfVlNfZmVhdHVyZV9oZAogIAogICMgU2lnbmlmaWNhbmNlIGFzc29jaWF0ZWQgd2l0aCBwLXZhbHVlCiAgcHZhbCA9IGZlYXR1cmVfaHNfVlNfZmVhdHVyZV9oZCRwLnZhbHVlCiAgCiAgc2lnbmlmaWNhbmNlID0gY2FzZV93aGVuKHB2YWwgPiAwLjA1IH4gIm5zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbCA+IDAuMDEgJiBwdmFsIDw9IDAuMDUgfiAiKiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWwgPiAwLjAwMSAmIHB2YWwgPD0gMC4wMSB+ICIqKiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWwgPD0gMC4wMDEgfiAiKioqIikKICAKICAjIFBsb3QKICBwID0gU2V1cmF0OjpWbG5QbG90KHNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgIHB0LnNpemUgPSAwLjMsIGZlYXR1cmVzID0gZmVhdHVyZSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gc2FtcGxlX3R5cGVfY29sb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gbmFtZXMoc2FtcGxlX3R5cGVfY29sb3JzKSkgKwogICAgIyBTaWduaWZpY2FuY2UgYmFyCiAgICBnZ3Bsb3QyOjpnZW9tX3NlZ21lbnQoZGF0YSA9IGRhdGEuZnJhbWUoMSksIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gMSwgeGVuZCA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IG1heChmZWF0dXJlX2V4cHIpKzAuMywgeWVuZCA9IG1heChmZWF0dXJlX2V4cHIpKzAuMykgKwogICAgZ2dwbG90Mjo6Z2VvbV9sYWJlbChkYXRhID0gZGF0YS5mcmFtZSgxKSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgeCA9IDEuNSwgeSA9IG1heChmZWF0dXJlX2V4cHIpKzAuMzUsCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gc2lnbmlmaWNhbmNlLAogICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gTkEsIGxhYmVsLnNpemUgPSAwKSArCiAgICBnZ3Bsb3QyOjpsaW1zKHkgPSBjKDAsIG1heChmZWF0dXJlX2V4cHIpKzAuNCkpICsKICAgICMgVGhlbWUKICAgIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICAKICByZXR1cm4ocCkKfSkKCm5hbWVzKHBsb3RfbGlzdCkgPSBmZWF0dXJlc19vaQpwbG90X2xpc3QKYGBgCiogaGVhdG1hcAoKSGVhdG1hcCBiZXR3ZWVuIEhTIGFuZCBIRCA6CgpgYGB7ciBoZnNjc19oZWF0bWFwX291ciwgZmlnLndpZHRoID0gNS41LCBmaWcuaGVpZ2h0ID0gMTB9CiMgZmVhdHVyZXNfb2kgPSBsaXN0X3Jlc3VsdHMkSFNfdnNfSEQgJT4lCiMgICBkcGx5cjo6ZmlsdGVyKHBjdC4xID4gMC41IHwgcGN0LjIgPiAwLjUpICU+JQojICAgZHBseXI6OmFycmFuZ2UoLWF2Z19sb2dGQywgLXBjdC4xLCAtcGN0LjIpICU+JQojICAgcm93bmFtZXMoKQojIGZlYXR1cmVzX29pID0gZmVhdHVyZXNfb2lbIWdyZXBsKGZlYXR1cmVzX29pLCBwYXR0ZXJuID0gIl5SUCIpXQoKZmVhdHVyZXNfb2kgPSBjKCJJRklUTTMiLCAiTUlGIiwgIkhMQS1DIiwgIkxNQ0QxIiwgIlRHRkIyIiwgIkNZUDFCMSIsCiAgICAgICAgICAgICAgICAiS1JUNkIiLCAiV0lGMSIsICJIU1BBMUEiLCAiQ09NVCIsICJDSVNEMSIsICJQUk5QIiwgICJDRDgxIiwKICAgICAgICAgICAgICAgICJITEEtQSIsICJCMk0iLCAiUFBJQSIsICJQWUNBUkQiLCAiQ1NBRCIsICJQRk4xIiwgIkhJRjFBIiwKICAgICAgICAgICAgICAgICJCQVNQMSIsICJDQVYxIiwgIlpORjMwMiIsICJMTU80IiwgIlNFUFQxMSIsICJIVFJBMSIsCiAgICAgICAgICAgICAgICAiR1BNNkEiLCAiU09YNCIsICJaRlAzNkwyIiwgIlJIT0IiLCAiQ0xETjEiLCAiRFVTUDEiLCAiVEJYMSIsCiAgICAgICAgICAgICAgICAiQVRGMyIsICJERElUNCIsICJUWE5JUCIsICJCVEcyIiwgIlNHSzEiLCAiS0xGNiIsICJMR1I1IikKCiMgTWF0cml4Cm1hdF9leHByID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc29iaikKbWF0X2V4cHIgPSBtYXRfZXhwcltmZWF0dXJlc19vaSwgXQptYXRfZXhwciA9IE1hdHJpeDo6dChtYXRfZXhwcikKbWF0X2V4cHIgPSBjYmluZChtYXRfZXhwciwgc29iaiRwZXJjZW50Lm10KQpjb2xuYW1lcyhtYXRfZXhwcilbbmNvbChtYXRfZXhwcildID0gInBlcmNlbnQucmIiCm1hdF9leHByID0gZHludXRpbHM6OnNjYWxlX3F1YW50aWxlKG1hdF9leHByKSAjIGJldHdlZW4gMCBhbmQgMQptYXRfZXhwciA9IE1hdHJpeDo6dChtYXRfZXhwcikKZGltKG1hdF9leHByKSAjIGdlbmVzIHggY2VsbHMKCiMjIENvbG9ycwpsaXN0X2NvbG9ycyA9IGxpc3QoKQoKIyBIZWF0bWFwCmxpc3RfY29sb3JzW1siZXhwcmVzc2lvbiJdXSA9IHJldihSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwobmFtZSA9ICJSZEJ1IiwgbiA9IDkpKQoKIyBTYW1wbGUgYW5ub3RhdGlvbiAodG9wIGFubm90YXRpb24pCmxpc3RfY29sb3JzW1sic2FtcGxlX3R5cGUiXV0gPSBzYW1wbGVfdHlwZV9jb2xvcnMKbGlzdF9jb2xvcnNbWyJzYW1wbGVfaWRlbnRpZmllciJdXSA9IHNldE5hbWVzKG5tID0gc2FtcGxlX2luZm8kc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaW5mbyRjb2xvcikKIyBsaXN0X2NvbG9yc1tbInNldXJhdF9jbHVzdGVycyJdXSA9IHNldE5hbWVzKGFxdWFyaXVzOjpnZ19jb2xvcl9odWUobGVuZ3RoKGxldmVscyhzb2JqJHNldXJhdF9jbHVzdGVycykpKSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5tID0gbGV2ZWxzKHNvYmokc2V1cmF0X2NsdXN0ZXJzKSkKIyBDZWxscyBvcmRlcgpjb2x1bW5fb3JkZXIgPSBzb2JqQG1ldGEuZGF0YSAlPiUKICBkcGx5cjo6YXJyYW5nZShzYW1wbGVfdHlwZSwgc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIHJvd25hbWVzKCkKY29sdW1uX29yZGVyID0gbWF0Y2goY29sdW1uX29yZGVyLCByb3duYW1lcyhzb2JqQG1ldGEuZGF0YSkpCgojIEhlYXRtYXAKaGFfdG9wID0gSGVhdG1hcEFubm90YXRpb24oc2FtcGxlX3R5cGUgPSBzb2JqJHNhbXBsZV90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRlbnRpZmllciA9IHNvYmokc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICMgY2x1c3RlcnMgPSBzb2JqJHNldXJhdF9jbHVzdGVycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gbGlzdChzYW1wbGVfdHlwZSA9IGxpc3RfY29sb3JzW1sic2FtcGxlX3R5cGUiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2lkZW50aWZpZXIgPSBsaXN0X2NvbG9yc1tbInNhbXBsZV9pZGVudGlmaWVyIl1dCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjbHVzdGVycyA9IGxpc3RfY29sb3JzW1sic2V1cmF0X2NsdXN0ZXJzIl1dCiAgICAgICAgICAgICAgICAgICAgICAgICAgICkpCgoKIyBnMSA6IFJFQUNUT01FX0NZVE9LSU5FX1NJR05BTElOR19JTl9JTU1VTkVfU1lTVEVNCiMgZzIgOiBHT0JQX0FQT1BUT1RJQ19QUk9DRVNTCmcxX2dlbmVzID0gYygiQjJNIiwgIkhMQS1DIiwgIkhMQS1BIiwgIk1JRiIsICJQUElBIiwgIkpVTkIiLCAiSUZJVE0zIikKZzJfZ2VuZXMgPSBjKCJKVU4iLCAiQVRGMyIsICJCVEcyIiwgIlJIT0IiLCAiTkZLQklBIiwgIlNHSzEiLCAiS0xGOSIsCiAgICAgICAgICAgICAiQ0FWMSIsICJERElUNCIsICJQREs0IiwgIlRYTklQIiwgIlJORjExNTIiLCAiVExFMSIpCmhhX3JpZ2h0ID0gZGF0YS5mcmFtZShnZW5lcyA9ICBjKGZlYXR1cmVzX29pLCAicGVyY2VudC5yYiIpLCByb3duYW1lcyA9IGMoZmVhdHVyZXNfb2ksICJwZXJjZW50LnJiIikpCmhhX3JpZ2h0JGdyb3VwID0gY2FzZV93aGVuKGhhX3JpZ2h0JGdlbmVzICVpbiUgZzFfZ2VuZXMgfiAiUkVBQ1RPTUVfQ1lUT0tJTkVfU0lHTkFMSU5HX0lOX0lNTVVORV9TWVNURU0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICBoYV9yaWdodCRnZW5lcyAlaW4lIGcyX2dlbmVzIH4gIkdPQlBfQVBPUFRPVElDX1BST0NFU1MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIm90aGVycyIpCgpsaXN0X2NvbG9yc1tbImdyb3VwIl1dID0gc2V0TmFtZXMoCiAgbm0gPSBjKCJSRUFDVE9NRV9DWVRPS0lORV9TSUdOQUxJTkdfSU5fSU1NVU5FX1NZU1RFTSIsICJHT0JQX0FQT1BUT1RJQ19QUk9DRVNTIiwgIm90aGVycyIpLAogIGMoInJlZCIsICJibGFjayIsICJncmF5OTAiKSkKCmhhX3JpZ2h0ID0gSGVhdG1hcEFubm90YXRpb24oZ3JvdXAgPSBoYV9yaWdodCRncm91cCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBsaXN0KGdyb3VwID0gbGlzdF9jb2xvcnNbWyJncm91cCJdXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hpY2ggPSAicm93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2Fubm90YXRpb25fbmFtZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfbGVnZW5kID0gVFJVRSkKCiMgSGVhdG1hcApodCA9IEhlYXRtYXAoYXMubWF0cml4KG1hdF9leHByKSwKICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCh0aXRsZSA9ICJFeHByZXNzaW9uIiwgYXQgPSBjKDAsIDEpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJsb3ciLCAiaGlnaCIpKSwKICAgICAgICAgICAgIGNvbCA9IGxpc3RfY29sb3JzW1siZXhwcmVzc2lvbiJdXSwKICAgICAgICAgICAgIHRvcF9hbm5vdGF0aW9uID0gaGFfdG9wLAogICAgICAgICAgICAgcmlnaHRfYW5ub3RhdGlvbiA9IGhhX3JpZ2h0LAogICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgIGNvbHVtbl9vcmRlciA9IGNvbHVtbl9vcmRlciwKICAgICAgICAgICAgIGNvbHVtbl9nYXAgPSB1bml0KDIsICJtbSIpLAogICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRkFMU0UsCiAgICAgICAgICAgICBzaG93X3Jvd19kZW5kID0gRkFMU0UsCiAgICAgICAgICAgICByb3dfdGl0bGUgPSBOVUxMLAogICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IDEwLCBmb250ZmFjZSA9ICJwbGFpbiIpLAogICAgICAgICAgICAgdXNlX3Jhc3RlciA9IEZBTFNFLAogICAgICAgICAgICAgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICBib3JkZXIgPSBUUlVFKQoKQ29tcGxleEhlYXRtYXA6OmRyYXcoaHQsCiAgICAgICAgICAgICAgICAgICAgIG1lcmdlX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3NpZGUgPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9sZWdlbmRfc2lkZSA9ICJib3R0b20iKQpgYGAKCiMjIEltbXVuZSBjZWxscwoKV2Ugc2VsZWN0IHRoZSBkYXRhc2V0czoKCmBgYHtyIGltbXVuZV9kYXRhc2V0X291cn0Kc29ial9hdGxhcyA9IGxpc3RfZGF0YSRvdXJfYXRsYXMkc29iagpuYW1lMkRfYXRsYXMgPSBsaXN0X2RhdGEkb3VyX2F0bGFzJG5hbWUyRApzb2JqID0gbGlzdF9kYXRhJGltbXVuZSRzb2JqCm5hbWUyRCA9IGxpc3RfZGF0YSRpbW11bmUkbmFtZTJECgpjZWxsX3R5cGVfb2ZfaW50ZXJlc3QgPSBjKCJDRDQgVCBjZWxscyIsICJDRDggVCBjZWxscyIsICJMYW5nZXJoYW5zIGNlbGxzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAibWFjcm9waGFnZXMiLCAiQiBjZWxscyIsICJwcm9saWZlcmF0aXZlIikKYGBgCgpMb2NhdGlvbiBvZiBjZWxscyBvbiB0aGUgd2hvbGUgYXRsYXM6CgpgYGB7ciBpbW11bmVfbG9jYXRpb25fb3VyLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMn0Kc29ial9hdGxhcyRjZWxsX2JjID0gY29sbmFtZXMoc29ial9hdGxhcykKc29iaiRjZWxsX2JjID0gY29sbmFtZXMoc29iaikKc29ial9hdGxhcyRpc19vZl9pbnRlcmVzdCA9IGRwbHlyOjpsZWZ0X2pvaW4oc29ial9hdGxhc0BtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgInBlcmNlbnQubXQiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvYmpAbWV0YS5kYXRhWywgYygiY2VsbF9iYyIsICJjbHVzdGVyX3R5cGUiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gImNlbGxfYmMiKVssICJjbHVzdGVyX3R5cGUiXQoKU2V1cmF0OjpEaW1QbG90KHNvYmpfYXRsYXMsIHJlZHVjdGlvbiA9IG5hbWUyRF9hdGxhcywgcHQuc2l6ZSA9IDAuMDAwMDAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiaXNfb2ZfaW50ZXJlc3QiLCBvcmRlciA9ICJUUlVFIikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKGNvbG9yX21hcmtlcnNbY2VsbF90eXBlX29mX2ludGVyZXN0XSwgYmdfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKGNlbGxfdHlwZV9vZl9pbnRlcmVzdCwgTkEpLCBuYS52YWx1ZSA9IGJnX2NvbG9yKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCiogY2VsbCB0eXBlIGFubm90YXRpb24KCmBgYHtyIGNlbGxfdHlwZV9pbW11bmVfb3VyLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCiogY2x1c3RlciB0eXBlIGFubm90YXRpb24KCmBgYHtyIGNsdXN0ZXJfdHlwZV9pbW11bmVfb3VyLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNsdXN0ZXJfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCiogZmVhdHVyZSBwbG90cyBmb3IgZ2VuZXMgcmVsYXRlZCB0byBUQ1IgYWxwaGEKCmBgYHtyIGdlbmVfaW1tdW5lX291ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4fQpmZWF0dXJlc19vaSA9IGMoIkNEM0UiLCAiQ0Q0IiwgIkNEOEEiLAogICAgICAgICAgICAgICAgc29ydChncmVwKHJvd25hbWVzKHNvYmopLCBwYXR0ZXJuID0gIl5UUkFbQ3xWXSIsIHZhbHVlID0gVFJVRSkpKQoKcGxvdF9saXN0ID0gbGFwcGx5KGZlYXR1cmVzX29pLCBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIHAgPSBTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmosIGZlYXR1cmVzID0gb25lX2dlbmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gbmFtZTJELCBvcmRlciA9IFRSVUUpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSkgKwogICAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogICAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgICBTZXVyYXQ6Ok5vTGVnZW5kKCkKICAKICByZXR1cm4ocCkKfSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5yb3cgPSA0KQpgYGAKCiogZmVhdHVyZSBwbG90cyBzcGxpdCBieSBzYW1wbGUgdHlwZSAoSFMgLyBIRCkKCmBgYHtyIGdlbmVfc3BsaXQsIGltbXVuZV9vdXIsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSAyfQpmZWF0dXJlc19vaSA9IGRhdGEuZnJhbWUocG9wdWxhdGlvbiA9IGMoImFsbCBpbW11bmUgY2VsbHMiLCByZXAoIm1hY3JvcGhhZ2VzIiwgMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJDRDQgVCBjZWxscyIsIDMpLCByZXAoIkNEOCBUIGNlbGxzIiwgMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZSA9IGMoIklMNiIsICJJTDFCIiwgIlRORiIsICJHWk1BIiwgIklGTkciLCAiSUwxN0EiLCAiUFJGMSIsICJHWk1CIikpCmZlYXR1cmVzX29pCgpwYXRjaHdvcmtfbGlzdCA9IGxhcHBseShmZWF0dXJlc19vaSRmZWF0dXJlLCBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIHBsb3RfbGlzdCA9IGFxdWFyaXVzOjpwbG90X3NwbGl0X2RpbXJlZChzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBuYW1lMkQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwbGl0X2J5ID0gInNhbXBsZV90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JfYnkgPSBvbmVfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZ19jb2xvciA9IGJnX2NvbG9yKQogIAogIHAgPSBwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBucm93ID0gMSkgKwogICAgcGF0Y2h3b3JrOjpwbG90X2xheW91dChndWlkZXMgPSAiY29sbGVjdCIpCiAgCiAgcmV0dXJuKHApCn0pCm5hbWVzKHBhdGNod29ya19saXN0KSA9IGZlYXR1cmVzX29pJGZlYXR1cmUKCnBhdGNod29ya19saXN0CmBgYAoKKiB2aW9saW4gcGxvdHMgd2l0aGluIGEgc3BlY2lmaWMgcG9wdWxhdGlvbgoKYGBge3IgZ2VuZV9zcGxpdF9pbW11bmVfb3VyLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gM30KcGxvdF9saXN0ID0gbGFwcGx5KGMoMTpucm93KGZlYXR1cmVzX29pKSksIEZVTiA9IGZ1bmN0aW9uKGkpIHsKICBvbmVfZ2VuZSA9IGZlYXR1cmVzX29pJGZlYXR1cmVbaV0KICBwb3B1bGF0aW9uID0gZmVhdHVyZXNfb2kkcG9wdWxhdGlvbltpXQogIAogICMgU3Vic2V0IG9iamVjdAogIGlmIChwb3B1bGF0aW9uICE9ICJhbGwgaW1tdW5lIGNlbGxzIikgewogICAgc3Vic29iaiA9IHN1YnNldChzb2JqLCBjbHVzdGVyX3R5cGUgJWluJSBwb3B1bGF0aW9uKQogIH0gZWxzZSB7CiAgIHN1YnNvYmogPSBzb2JqIAogIH0KICAKICAjIHQtdGVzdCBiZXR3ZWVuIHNhbXBsZSB0eXBlCiAgZmVhdHVyZV9leHByID0gc3Vic29iakBhc3NheXMkUk5BQGRhdGFbb25lX2dlbmUsIF0KICBmZWF0dXJlX2hzID0gZmVhdHVyZV9leHByW3N1YnNvYmokc2FtcGxlX3R5cGUgPT0gIkhTIl0KICBmZWF0dXJlX2hkID0gZmVhdHVyZV9leHByW3N1YnNvYmokc2FtcGxlX3R5cGUgPT0gIkhEIl0KICBmZWF0dXJlX2hzX1ZTX2ZlYXR1cmVfaGQgPSBzdGF0czo6dC50ZXN0KGZlYXR1cmVfaHMsIGZlYXR1cmVfaGQpCiAgZmVhdHVyZV9oc19WU19mZWF0dXJlX2hkCiAgCiAgIyBTaWduaWZpY2FuY2UgYXNzb2NpYXRlZCB3aXRoIHAtdmFsdWUKICBwdmFsID0gZmVhdHVyZV9oc19WU19mZWF0dXJlX2hkJHAudmFsdWUKICAKICBzaWduaWZpY2FuY2UgPSBjYXNlX3doZW4ocHZhbCA+IDAuMDUgfiAibnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsID4gMC4wMSAmIHB2YWwgPD0gMC4wNSB+ICIqIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbCA+IDAuMDAxICYgcHZhbCA8PSAwLjAxIH4gIioqIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbCA8PSAwLjAwMSB+ICIqKioiKQogIAogICMgUGxvdAogIHAgPSBTZXVyYXQ6OlZsblBsb3Qoc3Vic29iaiwgZ3JvdXAuYnkgPSAic2FtcGxlX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgcHQuc2l6ZSA9IDAuMywgZmVhdHVyZXMgPSBvbmVfZ2VuZSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gc2FtcGxlX3R5cGVfY29sb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gbmFtZXMoc2FtcGxlX3R5cGVfY29sb3JzKSkgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHBvcHVsYXRpb24sCiAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gb25lX2dlbmUpICsKICAgICMgU2lnbmlmaWNhbmNlIGJhcgogICAgZ2dwbG90Mjo6Z2VvbV9zZWdtZW50KGRhdGEgPSBkYXRhLmZyYW1lKDEpLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IDEsIHhlbmQgPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBtYXgoZmVhdHVyZV9leHByKSswLjMsIHllbmQgPSBtYXgoZmVhdHVyZV9leHByKSswLjMpICsKICAgIGdncGxvdDI6Omdlb21fbGFiZWwoZGF0YSA9IGRhdGEuZnJhbWUoMSksIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgIHggPSAxLjUsIHkgPSBtYXgoZmVhdHVyZV9leHByKSswLjM1LAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHNpZ25pZmljYW5jZSwKICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IE5BLCBsYWJlbC5zaXplID0gMCkgKwogICAgZ2dwbG90Mjo6bGltcyh5ID0gYygwLCBtYXgoZmVhdHVyZV9leHByKSswLjQpKSArCiAgICAjIFRoZW1lCiAgICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICAKICByZXR1cm4ocCkKfSkKbmFtZXMocGxvdF9saXN0KSA9IGZlYXR1cmVzX29pJGZlYXR1cmUKCnBsb3RfbGlzdApgYGAKCgoqIGhlYXRtYXAgbWFjcm9waGFnZXMKCmBgYHtyIGhlYXRtYXBfaW1tdW5lX21hY3JvcGhhZ2VzLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNS4yfQpzdWJzb2JqID0gc3Vic2V0KHNvYmosIGNsdXN0ZXJfdHlwZSA9PSAibWFjcm9waGFnZXMiKQpmZWF0dXJlc19vaSA9IGMoIklMMUIiLCAiVE5GIiwKICAgICAgICAgICAgICAgICJITEEtRFFBMiIsICJITEEtRFBBMSIsICJITEEtRFJCNSIsCiAgICAgICAgICAgICAgICAiSExBLUEiLCAiSExBLUMiLCAiQjJNIiwKICAgICAgICAgICAgICAgICJDMVFBIiwgIkMxUUIiLCAiQzFRQyIpCgojIE1hdHJpeAptYXRfZXhwciA9IFNldXJhdDo6R2V0QXNzYXlEYXRhKHN1YnNvYmopCm1hdF9leHByID0gbWF0X2V4cHJbZmVhdHVyZXNfb2ksIF0KbWF0X2V4cHIgPSBNYXRyaXg6OnQobWF0X2V4cHIpCm1hdF9leHByID0gZHludXRpbHM6OnNjYWxlX3F1YW50aWxlKG1hdF9leHByKSAjIGJldHdlZW4gMCBhbmQgMQptYXRfZXhwciA9IE1hdHJpeDo6dChtYXRfZXhwcikKZGltKG1hdF9leHByKSAjIGdlbmVzIHggY2VsbHMKIyMgQ29sb3JzCmxpc3RfY29sb3JzID0gbGlzdCgpCgojIEhlYXRtYXAKbGlzdF9jb2xvcnNbWyJleHByZXNzaW9uIl1dID0gcmV2KFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbChuYW1lID0gIlJkQnUiLCBuID0gOSkpCgojIFNhbXBsZSBhbm5vdGF0aW9uICh0b3AgYW5ub3RhdGlvbikKbGlzdF9jb2xvcnNbWyJzYW1wbGVfdHlwZSJdXSA9IHNhbXBsZV90eXBlX2NvbG9ycwpsaXN0X2NvbG9yc1tbInNhbXBsZV9pZGVudGlmaWVyIl1dID0gc2V0TmFtZXMobm0gPSBzYW1wbGVfaW5mbyRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pbmZvJGNvbG9yKQojIENlbGxzIG9yZGVyCmNvbHVtbl9vcmRlciA9IHN1YnNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjphcnJhbmdlKHNhbXBsZV90eXBlLCBzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgcm93bmFtZXMoKQpjb2x1bW5fb3JkZXIgPSBtYXRjaChjb2x1bW5fb3JkZXIsIHJvd25hbWVzKHN1YnNvYmpAbWV0YS5kYXRhKSkKCiMgSGVhdG1hcApoYV90b3AgPSBIZWF0bWFwQW5ub3RhdGlvbihzYW1wbGVfdHlwZSA9IHN1YnNvYmokc2FtcGxlX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZGVudGlmaWVyID0gc3Vic29iaiRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gbGlzdChzYW1wbGVfdHlwZSA9IGxpc3RfY29sb3JzW1sic2FtcGxlX3R5cGUiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2lkZW50aWZpZXIgPSBsaXN0X2NvbG9yc1tbInNhbXBsZV9pZGVudGlmaWVyIl1dKSkKCiMgSGVhdG1hcApodCA9IEhlYXRtYXAoYXMubWF0cml4KG1hdF9leHByKSwKICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCh0aXRsZSA9ICJFeHByZXNzaW9uIiwgYXQgPSBjKDAsIDEpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJsb3ciLCAiaGlnaCIpKSwKICAgICAgICAgICAgIGNvbCA9IGxpc3RfY29sb3JzW1siZXhwcmVzc2lvbiJdXSwKICAgICAgICAgICAgIHRvcF9hbm5vdGF0aW9uID0gaGFfdG9wLAogICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgIGNvbHVtbl9vcmRlciA9IGNvbHVtbl9vcmRlciwKICAgICAgICAgICAgIGNvbHVtbl9nYXAgPSB1bml0KDIsICJtbSIpLAogICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRkFMU0UsCiAgICAgICAgICAgICByb3dfdGl0bGUgPSBOVUxMLAogICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IDE0LCBmb250ZmFjZSA9ICJwbGFpbiIpLAogICAgICAgICAgICAgdXNlX3Jhc3RlciA9IEZBTFNFLAogICAgICAgICAgICAgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICBib3JkZXIgPSBUUlVFKQoKQ29tcGxleEhlYXRtYXA6OmRyYXcoaHQsCiAgICAgICAgICAgICAgICAgICAgIG1lcmdlX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3NpZGUgPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9sZWdlbmRfc2lkZSA9ICJib3R0b20iKQpgYGAKCiogaGVhdG1hcCBUIGNlbGxzCgpgYGB7ciBoZWF0bWFwX2ltbXVuZV90X2NlbGxzLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0Kc3Vic29iaiA9IHN1YnNldChzb2JqLCBjbHVzdGVyX3R5cGUgPT0gIkNENCBUIGNlbGxzIikKZmVhdHVyZXNfb2kgPSBjKCJHWk1BIiwgIktMUkIxIiwgIkJURzEiLCAiWkZQMzYiLCAiTkZLQklBIiwgIlRYTklQIiwgIkNYQ1I0IiwgIklGTkciLCAiSUwxN0EiKQoKIyBNYXRyaXgKbWF0X2V4cHIgPSBTZXVyYXQ6OkdldEFzc2F5RGF0YShzdWJzb2JqKQptYXRfZXhwciA9IG1hdF9leHByW2ZlYXR1cmVzX29pLCBdCm1hdF9leHByID0gTWF0cml4Ojp0KG1hdF9leHByKQptYXRfZXhwciA9IGR5bnV0aWxzOjpzY2FsZV9xdWFudGlsZShtYXRfZXhwcikgIyBiZXR3ZWVuIDAgYW5kIDEKbWF0X2V4cHIgPSBNYXRyaXg6OnQobWF0X2V4cHIpCmRpbShtYXRfZXhwcikgIyBnZW5lcyB4IGNlbGxzCiMjIENvbG9ycwpsaXN0X2NvbG9ycyA9IGxpc3QoKQoKIyBIZWF0bWFwCmxpc3RfY29sb3JzW1siZXhwcmVzc2lvbiJdXSA9IHJldihSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwobmFtZSA9ICJSZEJ1IiwgbiA9IDkpKQoKIyBTYW1wbGUgYW5ub3RhdGlvbiAodG9wIGFubm90YXRpb24pCmxpc3RfY29sb3JzW1sic2FtcGxlX3R5cGUiXV0gPSBzYW1wbGVfdHlwZV9jb2xvcnMKbGlzdF9jb2xvcnNbWyJzYW1wbGVfaWRlbnRpZmllciJdXSA9IHNldE5hbWVzKG5tID0gc2FtcGxlX2luZm8kc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaW5mbyRjb2xvcikKIyBDZWxscyBvcmRlcgpjb2x1bW5fb3JkZXIgPSBzdWJzb2JqQG1ldGEuZGF0YSAlPiUKICBkcGx5cjo6YXJyYW5nZShzYW1wbGVfdHlwZSwgc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIHJvd25hbWVzKCkKY29sdW1uX29yZGVyID0gbWF0Y2goY29sdW1uX29yZGVyLCByb3duYW1lcyhzdWJzb2JqQG1ldGEuZGF0YSkpCgojIEhlYXRtYXAKaGFfdG9wID0gSGVhdG1hcEFubm90YXRpb24oc2FtcGxlX3R5cGUgPSBzdWJzb2JqJHNhbXBsZV90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRlbnRpZmllciA9IHN1YnNvYmokc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGxpc3Qoc2FtcGxlX3R5cGUgPSBsaXN0X2NvbG9yc1tbInNhbXBsZV90eXBlIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZGVudGlmaWVyID0gbGlzdF9jb2xvcnNbWyJzYW1wbGVfaWRlbnRpZmllciJdXSkpCgojIEhlYXRtYXAKaHQgPSBIZWF0bWFwKGFzLm1hdHJpeChtYXRfZXhwciksCiAgICAgICAgICAgICBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QodGl0bGUgPSAiRXhwcmVzc2lvbiIsIGF0ID0gYygwLCAxKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygibG93IiwgImhpZ2giKSksCiAgICAgICAgICAgICBjb2wgPSBsaXN0X2NvbG9yc1tbImV4cHJlc3Npb24iXV0sCiAgICAgICAgICAgICB0b3BfYW5ub3RhdGlvbiA9IGhhX3RvcCwKICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICBjb2x1bW5fb3JkZXIgPSBjb2x1bW5fb3JkZXIsCiAgICAgICAgICAgICBjb2x1bW5fZ2FwID0gdW5pdCgyLCAibW0iKSwKICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEZBTFNFLAogICAgICAgICAgICAgcm93X3RpdGxlID0gTlVMTCwKICAgICAgICAgICAgIHJvd19uYW1lc19ncCA9IGdyaWQ6OmdwYXIoZm9udHNpemUgPSAxNCwgZm9udGZhY2UgPSAicGxhaW4iKSwKICAgICAgICAgICAgIHVzZV9yYXN0ZXIgPSBGQUxTRSwKICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgYm9yZGVyID0gVFJVRSkKCkNvbXBsZXhIZWF0bWFwOjpkcmF3KGh0LAogICAgICAgICAgICAgICAgICAgICBtZXJnZV9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICBoZWF0bWFwX2xlZ2VuZF9zaWRlID0gImxlZnQiLAogICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2xlZ2VuZF9zaWRlID0gImxlZnQiKQpgYGAKCiMgU3VwcGxlbWVudGFyeSB0YWJsZXMKCkluIHRoaXMgc2VjdGlvbiwgd2Ugc2F2ZSBmaWxlcyB0byBhc3NvY2lhdGUgd2l0aCB0aGUgbWFudXNjcmlwdCwgYXMgc3VwcGxlbWVudGFyeSB0YWJsZXMuCgojIyBUYWJsZSBTMiAocGFja2FnZSB2ZXJzaW9uKQoKV2UgbG9hZCB0aGUgdGFibGUgOgoKYGBge3IgdHMyX2xvYWR9CnBhY2thZ2VfdmVyc2lvbiA9IHJlYWQudGFibGUocGFzdGUwKCIuIiwgIi9kYXRhL2luZm9fdG9faW5zdGFsbF8yMDIzXzA0XzE3LnR4dCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUpCmhlYWQocGFja2FnZV92ZXJzaW9uKQpgYGAKCkNvbHVtbnMgY29ycmVzcG9uZCB0byA6CgoqICoqb3JkZXIqKiA6IG9yZGVyIHRvIGluc3RhbGwgcGFja2FnZSBzdWNoIHRoYXQgb25lIG5ldmVyIG5lZWQgdG8gaW5zdGFsbCBkZXBlbmRlbmNpZXMKKiAqKnBhY2thZ2VfbmFtZSoqIDogcGFja2FnZSBuYW1lCiogKip2ZXJzaW9uKiogOiBpbnN0YWxsZWQgdmVyc2lvbiwgb24gdGhlIGxvY2FsIG1hY2hpbmUKKiAqKnVybCoqIDogdGhlIHBhY2thZ2Ugd2FzIGluc3RhbGxlZCBpbiB0aGUgU2luZ3VsYXJpdHkgY29udGFpbmVyIHVzaW5nIHRoaXMgdXJsCgpXZSBzYXZlIHRoaXMgdGFibGUsIGV4Y2VwdCB0aGUgbGFzdCBjb2x1bW4gKGp1c3QgZm9yIG1lKSA6CgpgYGB7ciB0czJfc2F2ZX0Kb3Blbnhsc3g6OndyaXRlLnhsc3gocGFja2FnZV92ZXJzaW9uWywgYygxOjQpXSwKICAgICAgICAgICAgICAgICAgICAgZmlsZSA9IHBhc3RlMCgiLiIsICIvZGF0YS9TdXBwbGVtZW50YXJ5IFRhYmxlIDIueGxzeCIpKQpgYGAKCiMjIFRhYmxlIFMzIChjZWxsIHR5cGUgYW5ub3RhdGlvbikKCldlIGxvYWQgdGhlIHRhYmxlIDoKCmBgYHtyIHRzM19sb2FkfQpjZWxsX21hcmtlcnMgPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi8xX21ldGFkYXRhL2hzX2hkX2NlbGxfbWFya2Vycy5yZHMiKSkKbGVuZ3RocyhjZWxsX21hcmtlcnMpCmBgYAoKV2Ugc2F2ZSB0aGlzIHRhYmxlLCBleGNlcHQgdGhlIGxhc3QgY29sdW1uIChqdXN0IGZvciBtZSkgOgoKYGBge3IgdHMzX3NhdmV9Cm9wZW54bHN4Ojp3cml0ZS54bHN4KGNlbGxfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgZmlsZSA9IHBhc3RlMCgiLiIsICIvZGF0YS9TdXBwbGVtZW50YXJ5IFRhYmxlIDMueGxzeCIpKQpgYGAKCiMgUiBTZXNzaW9uCgpgYGB7ciBzZXNzaW9uaW5mbywgZWNobyA9IEZBTFNFLCBmb2xkX291dHB1dCA9IFRSVUV9CnNlc3Npb25JbmZvKCkKYGBgCg==